1.名词认识
目标对象(TargetObject): 需要实现代理的目标对象
连接点(Joinpoint): 连接点是指那些被拦截到的点,在spring中,这些点指的是方法,因为spring只支持方法类型的连接点.
切入点(Pointcut): 切入点是指我们要对哪些Joinpoint进行拦截定义(方法增强)
通知(Advice): 通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知
织入(Weaver): 是指把增强方法应用到目标对象过程
介引(Introduction): 动态的为某个类增加或减少方法
切面(Aspect): 是切入点和通知(引介)的结合
代理(Proxy): 一个类被AOP织入增强后,就产生一个结果代理类
2.jdk实现代理
- 需要使用java.lang.reflect.Proxy代理类
- 需要代理的类必须实现接口,且只能代理接口中的方法
- 准备需要代理的类,并实现UserService接口
package service.impl;
import entity.User;
import service.UserService;
public class UserServiceImpl implements UserService{
@Override
public boolean addUser() {
System.out.println("往数据库里添加了一个user");
return true;
}
}
package service.impl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import service.UserService;
public class UserAdvice implements InvocationHandler{
private UserService us;
public UserAdvice(UserService us) {
super();
this.us = us;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("----------正在执行-----------");
Object obj=method.invoke(us, args);
System.out.println("----------执行完毕-----------");
return obj;
}
}
@org.junit.Test
public void load4(){
UserServiceImpl us1=new UserServiceImpl();
UserService us2=(UserService)Proxy.newProxyInstance(us1.getClass().getClassLoader(), us1.getClass().getInterfaces(),new UserAdvice(us1));
us2.addUser(new User("张三", 20));
}
----------正在执行-----------
往数据库里添加了一个user
----------执行完毕-----------
3.AOP实现方法增强(织入、代理)
- 添加jar包
aspectjweaver-1.6.9.jar
aopalliance-1.0.jar
org.springframework.aop-3.1.1.RELEASE.jar
- 需要增强的类
package service.impl;
import dao.UserDao;
import entity.User;
import service.UserService;
public class UserServiceImpl implements UserService{
private UserDao ud;
public void setUd(UserDao ud) {
this.ud = ud;
}
@Override
public boolean addUser(User user) {
return ud.insert(user);
}
}
- 关于切点的正则表达
execution(public * *(..)) :匹配任何公用方法
execution(* set*(..)) :匹配名称以“set”开头的方法
execution(* com.xyz.service.AccountService.*(..)) :匹配AccountService接口定义的任何方法
execution(* com.xyz.service.*.*(..)) :匹配指定包中所有类型中的所有方法
execution(* com.xyz.service..*.*(..)) :匹配指定包中以及子包中所有类型的所有方法
within(com.xyz.service.*) :匹配指定包中的所有类
within(com.xyz.service..*) :匹配指定包中及子包中的所有类
this(com.xyz.service.AccountService) :匹配实现AccountService接口的任何连接点
target(com.xyz.service.AccountService) :匹配目标对象实现AccountService接口的任何连接点
args(java.io.Serializable) :匹配任何接受单个参数的连接点,并且参数类型是java.io.Serializable类型
方式一:通过接口
MethodBeforeAdvice //前置增强(方法执行前执行)
AfterReturningAdvice //后置增强(方法执行后执行)
ThrowsAdvice //异常增强(方法中出现异常执行)
MethodInterceptor //环绕增强(对方法具有绝对控制权)
package aop;
import java.lang.reflect.Method;
import java.util.Arrays;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.log4j.Logger;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.ThrowsAdvice;
public class UserServiceLogger2 implements MethodBeforeAdvice,AfterReturningAdvice,ThrowsAdvice,MethodInterceptor{
private static final Logger logger=Logger.getLogger(UserServiceLogger1.class);
@Override
public void before(Method method, Object[] args, Object target)
throws Throwable {
logger.error("前置通知:目标类型"+target.getClass().getName()+"--目标方法:"+method.getName()+"--参数:"+Arrays.toString(args));
}
@Override
public void afterReturning(Object returnValue, Method arg1, Object[] args,
Object target) throws Throwable {
logger.error("后置通知:参数:"+Arrays.toString(args)+"--返回值:"+returnValue.toString());
}
public void afterThrowing(Method method, Object[] args, Object target, Exception ex){
logger.error("异常通知:参数:"+Arrays.toString(args)+"--异常:"+ex.getMessage());
}
@Override
public Object invoke(MethodInvocation inv) throws Throwable {
Object result=null;
try {
logger.error("【环绕前置通知】--参数个数:"+inv.getArguments().length);
result=inv.proceed();
logger.error("【环绕后置通知】--返回值:"+result.toString());
} catch (Throwable e) {
logger.error("【环绕异常通知】--异常信息:"+e.getMessage());
}finally{
logger.error("【环绕最终通知】");
}
return result;
}
}
<?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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<!-- 声明dao的bean -->
<bean id="ud" class="dao.impl.UserDaoImpl"></bean>
<!--需要增强类的bean对象-->
<bean id="us" class="service.impl.UserServiceImpl">
<property name="ud" ref="ud" />
</bean>
<!--增强类的bean对象-->
<bean id="usl" class="aop.UserServiceLogger2"></bean>
<!--增强核心配置-->
<aop:config>
<!--配置切点-->
<aop:pointcut expression="execution(* service..*UserServiceImpl.addUser(..))" id="pointcut"/>
<!--配置引用增强-->
<aop:advisor advice-ref="usl" pointcut-ref="pointcut"/>
</aop:config>
</beans>
方式二:通过配置文件实现
package aop;
import java.util.Arrays;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class UserServiceLogger1 {
private static final Logger logger=Logger.getLogger(UserServiceLogger1.class);
public void before(JoinPoint j){
logger.error("前置通知:目标类型"+j.getTarget().getClass().getName()+"--目标方法:"+j.getSignature().getName()+"--参数:"+Arrays.toString(j.getArgs()));
}
public void afterReturning(JoinPoint j,Object result){
logger.error("后置通知:参数:"+Arrays.toString(j.getArgs())+"--返回值:"+result.toString());
}
public void afterThrowing(JoinPoint j,Exception e){
logger.error("异常通知:参数:"+Arrays.toString(j.getArgs())+"--异常:"+e.getMessage());
}
public void after(JoinPoint j){
logger.error("最终通知,无论有没有异常都会执行");
}
public Object around(ProceedingJoinPoint j){
Object result=null;
try {
logger.error("【环绕前置通知】--参数个数:"+j.getArgs().length);
result=j.proceed();
logger.error("【环绕后置通知】--返回值:"+result.toString());
} catch (Throwable e) {
logger.error("【环绕异常通知】--异常信息:"+e.getMessage());
}finally{
logger.error("【环绕最终通知】");
}
return result;
}
}
<?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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<!-- 声明dao的bean -->
<bean id="ud" class="dao.impl.UserDaoImpl"></bean>
<!--需要增强类的bean对象-->
<bean id="us" class="service.impl.UserServiceImpl">
<property name="ud" ref="ud" />
</bean>
<!--增强类的bean对象-->
<bean id="usl" class="aop.UserServiceLogger1"></bean>
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut expression="execution(* service..*.*addUser(..))" id="pointcut"/>
<!-- 配置引用增强bean对象 -->
<aop:aspect ref="usl">
<!--环绕增强-->
<aop:around method="around" pointcut-ref="pointcut" />
<!--前置增强-->
<aop:before method="before" pointcut-ref="pointcut"/>
<!--后置增强 returning返回值属性:值必须与增强方法中的形参一致-->
<aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result" />
<!--异常增强 throwing异常属性:值必须与增强方法中的形参一致-->
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="e" />
<!--最终增强-->
<aop:after method="after" pointcut-ref="pointcut" />
</aop:aspect>
</aop:config>
</beans>
方式三:通过注解实现
@Aspect //定义切面的注解
@Before //定义前置增强方法的注解
@AfterReturning //定义后置增强方法的注解
@AfterThrowing //定义异常增强方法的注解
@After //定义最终增强方法的注解
@Around //定义环绕增强方法的注解
<?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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<!-- 开启自动织入配置 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!-- 配置自动扫描 -->
<context:component-scan base-package="dao.impl,service.impl,aop"></context:component-scan>
</beans>
package service.impl;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import dao.UserDao;
import entity.User;
import service.UserService;
@Service("us")
public class UserServiceImpl implements UserService{
@Resource(name="ud")
private UserDao ud;
public void setUd(UserDao ud) {
this.ud = ud;
}
@Override
public boolean addUser(User user) {
return ud.insert(user);
}
}
package aop;
import java.util.Arrays;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component("ul")
@Aspect
public class UserServiceLogger3 {
private static final Logger logger=Logger.getLogger(UserServiceLogger3.class);
@Before("within(service..*)")
public void before(JoinPoint j){
logger.error("前置通知:目标类型"+j.getTarget().getClass().getName()+"--目标方法:"+j.getSignature().getName()+"--参数:"+Arrays.toString(j.getArgs()));
}
@AfterReturning(pointcut="execution(* service..*UserServiceImpl.addUser(..))",returning="result")
public void afterReturning(JoinPoint j,Object result){
logger.error("后置通知:参数:"+Arrays.toString(j.getArgs())+"--返回值:"+result.toString());
}
@AfterThrowing(pointcut="execution(* service..*UserServiceImpl.addUser(..))",throwing="e")
public void afterThrowing(JoinPoint j,Exception e){
logger.error("异常通知:参数:"+Arrays.toString(j.getArgs())+"--异常:"+e.getMessage());
}
@After("execution(* service..*UserServiceImpl.addUser(..))")
public void after(JoinPoint j){
logger.error("最终通知");
}
@Around("execution(* service..*UserServiceImpl.addUser(..))")
public Object around(ProceedingJoinPoint j){
Object result=null;
try {
logger.error("【环绕前置通知】--参数个数:"+j.getArgs().length);
result=j.proceed();
logger.error("【环绕后置通知】--返回值:"+result.toString());
} catch (Throwable e) {
logger.error("【环绕异常通知】--异常信息:"+e.getMessage());
}finally{
logger.error("【环绕最终通知】");
}
return result;
}
}
4.SpringAOP代理模式认识
4.1.三种情况
- 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
- 如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
- 如果目标对象实现了接口,可以强制使用CGLIB实现AOP
4.2.如何强制使用CGLIB实现AOP?
- 强制使用CGLIB代理设置proxy-target-class属性值为"true"
<aop:config proxy-target-class="true"> </aop:config>
- 需要使用CGLIB代理和@AspectJ自动代理支持
<aop:aspectj-autoproxy proxy-target-class="true" />
4.3.JDK动态代理和CGLIB字节码生成的区别?
- JDK动态代理只能对实现了接口的类生成代理,而不能针对类
- CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以该类或方法最好不要声明成final