一、Spring注解装配bean
1、前言
Spring 容器支持多种形式的 Bean 的装配方式,如基于 XML 的 Bean 装配、基于 Annotation 的 Bean 装配和自动装配等。
Java 从 JDK 5.0 以后,提供了 Annotation(注解)功能,Spring 2.5 开始也提供了对 Annotation 技术的全面支持。
之前已经大致讲了XML方式和自动装配,其实相比这两者,更加推荐使用注解方式,因为前两种在面临Bean数量较多时,会导致 XML 配置文件过于臃肿,从而给维护和升级带来一定的困难。
2、常用的注解
要使用注解,首先要配置开启注解,可以选择扫描指定包或者自动扫描。
-
1)作用在类上的: 相当于声明bean,类似于< bean id=“user” class=“com.spring.User”>
- @Component: 是一个泛化的概念,仅仅表示一个组件(Bean),并且可以作用在任何层次。使用时只需将该注解标注在相应类上即可。
- @Repository: 用于将数据访问层(DAO层)的类标识为 Spring 中的 Bean。
- @Service: 通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean。
- @Controller: 通常作用在控制层(Action、web),用于将控制层的类标识为 Spring 中的 Bean。
- 注意:通常会@Controller(“student”)这样写,括号中为bean的名字,如果不写,会把小写的类名作为名字。
- @Scope:指定对象作用域。
-
2)作用在类里的: 相当于给bean注入值,类似于< property name=“student” ref=“student”/>
- @Autowired: 用于Bean的属性变量、属性的Set方法、非Set方法及构造函数,默认按照类型进行装配(类似byType)。
- @Resource: 其作用与 Autowired 一样。区别在于它是默认按照名称装配的(类似byName)。
- 注意:@Resource有两个重要属性name和type,指定name按照名称装配,指定type按照类型装配,我们可以组合使用@Resource(name=“user”,type=User.class)更加精确装配。如果都不指定直接@Resource,则先按名称装配,匹配不到再按照类型装配,如果都无法匹配,则抛出 NoSuchBeanDefinitionException 异常。
- @Qualifier: 按名称装配。常与 @Autowired 注解配合使用,当只用 @Autowired会产生一个问题,当一个类型有多个bean值的时候,会造成无法选择具体注入哪一个的情况,这个时候使用@Qualifier告诉spring具体去装配哪个对象。
二、AOP
面向切面编程(AOP)和面向对象编程(OOP)类似,也是一种编程模式。
面向切面的编程可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。添加在概念上独立于应用程序的业务逻辑。有各种这方面的例子,如日志记录、审计、声明式事务、安全性和缓存等。
1、关键术语
在开始之前必须要对AOP的一些术语有所了解
- Target(目标对象): 被代理对象,被通知的对象,被增强的类对象。
- JoinPoint(连接点): 目标对象中,所有可以增强的方法,就是spring允许你是通知(Advice)的地方,基本每个方法的前、后,或抛出异常是时都可以是连接点,spring只支持方法连接点。
- Pointcut(切入点): 指要对哪些 Joinpoint 进行拦截,即被拦截的连接点。
- Advice(通知/增强) : 指拦截到 Joinpoint 之后要做的事情,即对切入点通知/增强的内容。
- Weaving(织入): 指把增强代码应用到目标对象上,生成代理对象的过程。
- Proxy(代理): 将通知织入到目标对象之后形成的代理对象。
- aspect(切面): 切入点+通知
2、xml配置AOP
导入spring-aop + spring-aspects + aopalliance + aspectjweaver
AspectJ支持包下载地址:https://www.eclipse.org/aspectj/downloads.php
引用 aop 命名空间标签
userTarget是要增强的目标对象;
myAdvice是增强/通知的内容;
aop:config其实就是将通知织入目标对象;
pointcut定义切入点,要对哪些方法拦截,注意里边execution表达式: 第一个 * 号:表示返回类型, *号表示所有的类型。中间是包名,要一直配置到具体的方法。 * (…):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数;如果想优化的话可以把execution里的地址描述的清楚,范围小写,比如直接写到类,或方法。
aspect配置通知类型和切入点:myAdvice表示通知体,advice123表示拦截点,before表示用myAdvice里的before()方法作为aop:before前置通知。简单说:advice123的方法被myAdvice类里的before()方法拦截通知。
注意下环绕通知:
3、注解配置AOP
注解就省事多了,首先开启注解,配置织入
代理-AOP实现原理
1、什么是代理
代理(Proxy)是一种设计模式。提供了对目标对象另外的访问方式,即通过代理 访问目标对象。这样好处是可以在目标对象实现的基础上,增强额外的功能操作。 (扩展目标对象的功能)
代理模式的关键点:代理对象和目标对象
2、Spring的AOP有JDK动态代理和cglib动态代理两种实现方式
- 1、JDK 动态代理:要求被代理对象实现接口。通过 JDK 中的 java.lang.reflect.Proxy 类实现的:
当然,这样可能不是很清楚,此时可以把增强都写在MyAdvice1里,只要能实现就可以。
- 2、CGLIB 动态代理:CGLIB(Code Generation Library)是一个高性能开源的代码生成包,它被许多 AOP 框架所使用,其底层是通过使用一个小而快的字节码处理框架 ASM(Java 字节码操控框架)转换字节码并生成新的类。因此 CGLIB 要依赖于 ASM 的包,Spring 的核心包 spring-core已经集成了 CGLIB 所需要的包,所以在开发中不需要另外导入 ASM 的 JAR 包了。
通过上边可以看出:jdk代理关键在于代理类Proxy的静态方法 newProxyInstance()和实现InvocationHandler接口重写invoke方法;
CGLIB代理关键在于Enhancer类和实现MethodInterceptor接口重写intercept方法。
- 3、选谁:JDK 动态代理和 CGLIB 动态代理均是实现 Spring AOP 的基础。那么Spring AOP采用哪种代理方式?
- 从源码片段可以看出,是否使用 CGLIB 是在代码中进行判断的,判断条件是 config.isOptimize()、config.isProxyTargetClass() 和 hasNoUserSuppliedProxyInterfaces(config)。
- 其中,config.isOptimize() 与 config.isProxyTargetClass()默认返回都是 false,这种情况下判断结果就由hasNoUserSuppliedProxyInterfaces(config)的结果决定了。
- hasNoUserSuppliedProxyInterfaces(config) 就是在判断代理的对象是否有实现接口,有实现接口的话直接走 JDK 分支,即使用 JDK 的动态代理。
- 如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理实现 AOP;如果目标对象没有实现了接口,则采用 CGLIB 库,Spring 会自动在 JDK 动态代理和 CGLIB 动态代理之间转换。
3、静态代理
静态代理,是指程序运行前就已经存在了代理类的字节码文件,代理类和被代理类的关系在运行前就已经确定。
动态代理:动态代理类的字节码是在程序运行期间的动态生成,所以不存在代理类的字节码文件,代理类和被代理类的关系是在程序运行时确定的。
4、JDK代理和cglib代理对比:
- JDK动态代理通过JVM实现代理类字节码的创建,cglib通过ASM创建字节码;
- JDK动态代理要求被代理对象实现接口,cglib要求被代理对象未被final修饰(动态继承)
- JDK动态代理创建代理对象速度比cglib快
- cglib代理对象执行速度比JDK动态代理快