AOP、Spring注解实现自动注入
一、面向切面编程(AOP)
1、什么是AOP
AOP的目标: 让我们可以“专心做事”
AOP的原理:
1、将复杂的需求分解出不同方面,将散布在系统中的公共功能集中解决。
2、采用代理机制组装起来运行,在不改变原程序的基础上对代码段进行增强处理,增加新的功能。
通过动态代理实现AOP(采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行)。
AOP是一种思想,它与具体的实现技术无关,任何一种符合AOP的思想的技术实现,都可以看做是AOP的实现。通过java的动态代理机制,就可以很容易实现AOP的思想,实际上Spring的AOP也是建立在Java的代理机制上。——我们发现AOP实际上是由目标类的代理类实现的。AOP代理其实是由AOP框架动态生成的一个对象,该对象可作为目标对象使用。AOP代理包含了目标对象的全部方法,但是AOP代理中的方法与目标对象的方法存在差异, AOP方法在特定切入点添加了增强处理,并回调了目标对象的方法。
总结对AOP理解:
业务处理的主要流程就是核心关注点,与之关系不大的部分就是横切关注点。横切关注点的一个特点就是:他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事务处理。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。
所谓面向切面编程,是一种通过预编译和运行期动态代理的方式实现在不修改源代码的情况下给程序动态添加功能的技术。
2、AOP相关术语
增强(Advice): 又翻译成通知,定义了切面是什么以及何时使用,描述了切面要完成的工作和何时需要执行这个工作。是织入到目标类连接点上的一段程序代码。增强包含了用于添加到目标连接点上的一段执行逻辑,又包含了用于定位连接点的方位信息。(所以spring提供的增强接口都是带方位名:BeforeAdvice(表示方法调用前的位置)、AfterReturninAdvice(表示访问返回后的位置)、ThrowAdvice等等,所以只有结合切点和增强两者一起才能确定特定的连接点并实施增强逻辑)。增强又分前置增强、后置增强、环绕增强、异常抛出增强、最终增强等类型。
Advice:增强处理
切入点(Pointcut): Advice定义了切面要发生“故事”和时间,那么切入点就定义了“故事”发生的地点。例如某个类或者方法名,Spring中允许我们使用正则来指定。
连接点(Joinpoint): 切入点匹配的执行点称作连接点。如果说切入点是查询条件,那连接点就是被选中的具体的查询结果。程序执行的某个特定位置,程序能够应用增强代码的一个“时机”,比如方法调用或者特定异常抛出。
切面(Aspect): 切点和增强组成切面。它包括了横切逻辑的定义,也包括了连接点的定义。Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。
代理(Proxy): AOP框架创建的对象。一个类被AOP织入增强之后,就产生了一个结果类,它是融合了原类和增强逻辑的代理类。
目标对象(Target): 增强逻辑的织入的目标类。
织入(Weaving): 将增强添加到目标类具体连接点上的过程。
AOP有三种织入的方式:编译期织入、类装载期织入、动态代理织入(spring采用动态代理织入)。
面向切面编程主要关心两个问题,即:在什么位置,执行什么功能。Spring AOP是负责实施切面的框架,即由Spring AOP完成织入工作。
3、示例:使用AOP实现日志功能
1、需求:在业务方法执行前后添加日志功能
2、开发步骤
1、在项目中添加Spring AOP的jar文件
2、编写前置增强和后置增强实现日志功能。编写目标方法和增强处理。
3、编写Spring配置文件,对业务方法进行增强处理。在Spring配置文件中定义切入点,在切入点织入增强处理
4、编写代码获取带有增强处理的业务对象。
3、项目结构
4、总结
二、Spring注解实现自动注入
1、使用注解实现IOC
注解方式可以将Bean的定义信息和Bean实现类结合在一起,Spring提供的注解有:
@Component:实现Bean组件的定义。
@Repository :用于标注DAO类。
@Service :用于标注业务类。
@Controller :用于标注控制器类。
指明userDao是UserDaoImpl的实例名称:
@Repository("userDao")
public class UserDaoImpl implements UserDao {
…
}
上述与在XML配置文件中编写以下代码等效
<bean id="userDao" class="dao.impl.UserDaoImpl" />
使用@Autowired注解实现Bean的自动装配,默认按类型匹配,可以使用@Qualifier指定Bean的名称,对类的成员变量进行标注:
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
@Qualifier("userDao")
private UserDao dao; //对类的成员变量进行标注
……
}
也可以对方法的入参进行标注:
@Service("userService")
public class UserServiceImpl implements UserService {
private UserDao dao;
@Autowired
public void setDao((@Qualifier("userDao") UserDao dao) {
this.dao = dao;
}
……
}
配置文件中指定需要扫描的基类包,多个包可用逗号隔开:
<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"
xsi:schemaLocation="......
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<!-- 扫描包中注解标注的类 -->
<context:component-scan base-package="service,dao" />
</beans>
2、使用注解实现AOP
需求:使用注解来实现日志切面。
步骤:
(1) 使用注解定义前置增强和后置增强实现日志功能。
@Aspect:定义切面。
@Before:定义前置增强。
@AfterReturning:定义后置增强。
(2) 编写Spring配置文件,完成切面织入。
<aop:aspectj-autoproxy />:启用对于@AspectJ注解的支持
package aop;
import java.util.Arrays;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Pointcut;
/**
* 使用注解定义切面
*/
@Aspect
public class UserServiceLogger {
private static final Logger log = Logger.getLogger(UserServiceLogger.class);
@Pointcut("execution(* service.UserService.*(..))")
public void pointcut() { }
@Before("pointcut()")
public void before(JoinPoint jp) {
log.info("调用 " + jp.getTarget() + " 的 " + jp.getSignature().getName() + " 方法。方法入参:" + Arrays.toString(jp.getArgs()));
}
@AfterReturning(pointcut = "pointcut()", returning = "returnValue")
public void afterReturning(JoinPoint jp, Object returnValue) {
log.info("调用 " + jp.getTarget() + " 的 " + jp.getSignature().getName() + " 方法。方法返回值:" + returnValue);
}
}
配置文件代码:
<context:component-scan base-package="service,dao" />
<bean class="aop.UserServiceLogger"></bean>
<!-- 启用对于@AspectJ注解的支持 -->
<aop:aspectj-autoproxy />