Spring

Spring

什么是框架? 比如一个简历模板,你拿过来只需要把里面的内容改了就行,可以直接用

Spring的理念:使现有的技术更加容易使用,本身就是一个大杂烩,整合了现有的技术框架

Spring是什么:

  • spring是一个开源的免费的框架(容器)

  • spring是一个轻量级的、非入侵式的框架(就是他的引入不会导致原有的项目不能运行)

  • 控制反转(IOC),面向切面编程(AOP)

  • 支持事务的处理,对框架整合的支持(支持很多)

  • 所有的类都需要装配到bean里面,然后所有的bean呢,就需要通过容器来取

  1. UserDao 数据库接口
  2. UserDaoImpl 数据库实现类
  3. UserService 业务接口
  4. UserServiceImpl 业务实现类

在我们之前的业务中,只要用户需求发生改变,我们就要根据用户的需求去修改原代码

但是在我们使用了一个Set接口实现之后,就发生了革命性的改变

    private UserDao userDao;
    public void getUser() {
        userDao.getUser();
    }
//利用set进行动态实现值的注入	俗称依赖注入
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
  • 之前,对象的实例化创建交由给程序控制,控制权在程序员自己手里
  • 但是使用了set注入之后,程序就不再具有主动权,而是成为被动的接收对象,就将对象的创建和实例化过程交由用户自己来创建

这种思想,从本质上解决了问题,我们程序员就不用再去管理对象的创建了,大大降低了系统的耦合性,可以使我们更加专注与业务的实现和开发!这就是IOC的原型

框架之所以叫框架,是因为他帮我们做了很多东西,我们只需要进行使用就可以了

这个过程就叫控制反转:
控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的.
反转∶程序本身不创建对象,而变成被动的接收对象.依赖注入:就是利用set方法来进行注入的.
IOC是一种编程思想,由主动的编程变成被动的接收.
可以通过new ClassPathXmlApplicationContext去浏览一下底层源码.
我们彻底不用再程序中去改动了,要实现不同的操作,只需要在xml配置文件中进行修改,所谓的loC,一句话搞定:对象由Spring 来创建,管理,装配!

IOC创建对象的方式

  1. 使用无参构造对象,默认的
  2. 有参构造:用constructor-arg 标签 value赋值
    1. 下标赋值 index
    2. 通过类型创建 type
    3. 通过参数名设置 name

在程序执行的是,整个配置文件中的bean都会被创建,单例模式

Maven依赖

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.0.RELEASE</version>
        </dependency>
    </dependencies>

Spring配置

  1. 别名标签 alias 下面的name更高级
  2. bean标签中 有一个id是唯一标识符 class是类 name是别名 例如name="user1,user2"可以取多个别名(关键)
  3. import标签 属性resource 可以导入其他的Spring配置文件,可以将多个配置文件合并成一个,便于管理合作开发
    核心配置 applicationContext.xml

applicationContext.xml配置约束模板

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">

    <!-- 指定包下的注解就会生效 *通配符,代表所有包 ->
	<context:component-scan base-package="*.*"></context:component-scan>
    <!--bean就是java对象 , 由Spring创建和管理-->
</beans>
	<!-- 配置注解的支持 -->
    <context:annotaion-config/>
    <!-- 指定包下的注解就会生效 *通配符,代表所有包,有了这个就不需要上面那个了-->
	<context:component-scan base-package="*.*"></context:component-scan>

如何使用?

ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Student student = (Student) context.getBean("student");
//这样就不需要强转了
Student student = context.getBean("student",Student.class);
System.out.println(student.getName());

DI依赖注入

依赖:bean对象的创建依赖于容器 ; 注入:bean对象中的所有属性,都由容器来注入!

  1. 构造器注入
  2. set方式注入
  3. 拓展方式注入

不同类型的注入方式:

image-20220310172744387image-20220310172943728

引入命名空间:c和p

需要先导入约束

p命名空间,可以直接在bean标签上注入属性的值 p:属性名="value" 属性注入property

xmlns:p="http://www.springframework.org/schema/p"
image-20220310185938035

c命名空间 通过 c:属性名-ref=“value”来赋值 构造器注入 construct

xmlns:c="http://www.springframework.org/schema/c"

image-20220310185905990

bean的作用域scope

  • singleton单例模式 始终是一个对象 (单线程推荐使用)

    singleton单例模式
    手写一个单例模式?饿汉模式

    public class Singleton {   
     private static Singleton singleton = new Singleton();
     private Singleton() {};
     public static Singleton getInstance() {
         return singleton;    
    	}
    }
    
  • prototype原型模式 每次从容器中get的时候,都会产生一个新的bean (多线程推荐使用)

prototype原型模式

  • 其余的作用域:request、session、application 只能在web开发中使用到

Spring 的三种装配方式

  1. xml中的显式配置装配
  2. java代码实现的显式配置装配
  3. 隐式 实现装配
    1. byName:会自动在容器上下文中查找,和自己对象set方法后面的值对应的bean id !
      bean的id要唯一
    2. byType:会自动在容器上下文中查找,和自己对象属性类型相同的bean !
      bean的类型要唯一

注解

自动装配

  • @Autowired bytype对象必须存在(常用) @Resource byname找不到就用bytype
  • @Nullable 该字段可以为空

如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候、我们可以使用@Qualifier(value="xxx")去配置@Autowired的使用,指定一个唯一的bean对象注入

需要引入命名空间和支持
	xmlns:context="http://www.springframework.org/schema/context"
	<!-- 配置注解的支持 -->
    <context:component-scan base-package="*.*"></context:component-scan>
  • @Component 组件,写在类上,等价于spring创建了一个bean
    • dao @Repository
    • service @Service
    • controller @Controller
    • 他们四个的功能是一样的,就是便于我们分辨而已
  • @Value 写在实体类下属性上,进行赋值,类似于在bean中给属性赋值
  • @Scope("") 作用域,原型和单例

小结:

  • xml和注解:
    • xml更加万能,适用于任何场合!维护简单方便。注解不是自己类使用不了,维护相对复杂!xml与注解最佳实践:
    • xml 用来管理bean;
    • 注解只负责完成属性的注入;

代理

什么是代理?
什么是面向切面编程?
代理的作用是什么,为什么要代理?

静态代理:

代理,例如有这样一个关系,
租客,中介,房东
房东呢就想提供一个房源,不想管其他事情,而此时就有一个中介这么一个对象,他能帮你签合同,带租客看房等等。而租客想租房找不到房东的时候,就有中介这么个东西,他能代替房东发挥它的作用。而此时我们面对的对象就变成了中介,中介呢也就是做到了其中的一个代理的作用。

image-20220312132627151

动态代理:

  • 动态代理和静态代理的剧角色是一样的

  • 动态代理的代理类是动态生成的,不是我们直接写好的

  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理

    • 基于接口---jdk 动态代理 我们在这里使用
    • 基于类:cglib
    • java字节码实现
  • 动态代理代理的是接口

需要了解两个类:Proxy 代理,InvocationHandler 调用处理程序 invoke(调用,引用,执行)

每一个代理实例,他都有关联一个调用处理程序

我们也可以编写一个通用的动态代理实现的类!所有的代理对象设置为Object即可!

public class ProxyInvocationHandler implements InvocationHandler {
   private Object target;

   public void setTarget(Object target) {
       this.target = target;
  }

   //生成代理类
   public Object getProxy(){
       return Proxy.newProxyInstance(this.getClass().getClassLoader(),
               target.getClass().getInterfaces(),this);
  }

   // proxy : 代理类
   // method : 代理类的调用处理程序的方法对象.
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       log(method.getName());
       Object result = method.invoke(target, args);
       return result;
  }

   public void log(String methodName){
       System.out.println("执行了"+methodName+"方法");
  }

}

测试

public class Test {
   public static void main(String[] args) {
       //真实对象
       UserServiceImpl userService = new UserServiceImpl();
       //代理对象的调用处理程序
       ProxyInvocationHandler pih = new ProxyInvocationHandler();
       pih.setTarget(userService); //设置要代理的对象
       UserService proxy = (UserService)pih.getProxy(); //动态生成代理类!
       proxy.delete();
  }
}

Method是什么,就是我们要执行的目标对象的方法,

AOP

动态代理代理的是接口,

  • 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 ....
  • 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
  • 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
  • 目标(Target):被通知对象。
  • 代理(Proxy):向目标对象应用通知之后创建的对象。
  • 切入点(PointCut):切面通知 执行的 “地点”的定义。
  • 连接点(JointPoint):与切入点匹配的执行点。
  1. 三种方式

    • 通过 Spring API 实现
     <!--注册bean-->
       <bean id="userService" class="com.mao.service.UserServiceImpl"/>
       <bean id="log" class="com.mao.log.Log"/>
       <bean id="afterLog" class="com.mao.log.AfterLog"/>
    
       <!--aop的配置-->
       <aop:config>
           <!--切入点 expression:表达式匹配要执行的方法-->
           <aop:pointcut id="pointcut" expression="execution(* com.mao.service.UserServiceImpl.*(..))"/>
           <!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
           <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
           <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
       </aop:config>
    
    • 自定义类来实现Aop
    <!--第二种方式自定义实现-->
    <!--注册bean-->
    <bean id="diy" class="com.mao.config.DiyPointcut"/>
    
    <!--aop的配置-->
    <aop:config>
       <!--第二种方式:使用AOP的标签实现-->
       <aop:aspect ref="diy">
           <aop:pointcut id="diyPonitcut" expression="execution(* com.mao.service.UserServiceImpl.*(..))"/>
           <aop:before pointcut-ref="diyPonitcut" method="before"/>
           <aop:after pointcut-ref="diyPonitcut" method="after"/>
       </aop:aspect>
    </aop:config>
    
    • 使用注解实现

第一步:编写一个注解实现的增强类

package com.kuang.config;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class AnnotationPointcut {
   @Before("execution(* com.mao.service.UserServiceImpl.*(..))")
   public void before(){
       System.out.println("---------方法执行前---------");
  }

   @After("execution(* com.mao.service.UserServiceImpl.*(..))")
   public void after(){
       System.out.println("---------方法执行后---------");
  }

   @Around("execution(* com.mao.service.UserServiceImpl.*(..))")
   public void around(ProceedingJoinPoint jp) throws Throwable {
       System.out.println("环绕前");
       System.out.println("签名:"+jp.getSignature());
       //执行目标方法proceed
       Object proceed = jp.proceed();
       System.out.println("环绕后");
       System.out.println(proceed);
  }
}

第二步:在Spring配置文件中,注册bean,并增加支持注解的配置

<!--第三种方式:注解实现-->
<bean id="annotationPointcut" class="com.kuang.config.AnnotationPointcut"/>
<aop:aspectj-autoproxy/>
posted @   没有烦恼的猫猫  阅读(45)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~
点击右上角即可分享
微信分享提示