1. JDK动态代理—有接口情况
使用 JDK 动态代理,使用 Proxy 类里面的方法创建代理对象
创建接口,定义方法:
package cn.edu.xd.dao;
public interface UserDao {
public int add(int a,int b);
public void printMsg(String msg);
}
创建接口实现类,实现接口方法:
package cn.edu.xd.dao;
public class UserDaoImpl implements UserDao{
@Override
public int add(int a, int b) {
System.out.println("add()方法执行...");
return a+b;
}
@Override
public void printMsg(String msg) {
System.out.println("update()方法执行...");
System.out.println("msg:"+msg);
}
}
使用 Proxy 类创建接口代理对象:
package cn.edu.xd.proxy;
import cn.edu.xd.dao.UserDao;
import cn.edu.xd.dao.UserDaoImpl;
import cn.edu.xd.dao.UserDaoProxy;
import java.lang.reflect.Proxy;
public class JDKProxy {
public static void main(String[] args) {
Class[] interfaces={UserDao.class};//要实现的接口代理对象 即增强UserDao接口中的方法
UserDaoImpl userDaoImpl=new UserDaoImpl();
UserDao userDao= (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(),interfaces,new UserDaoProxy(userDaoImpl));
int result=userDao.add(1,2);
System.out.println("result:"+result);
userDao.printMsg("hello");
}
}
package cn.edu.xd.dao;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;
public class UserDaoProxy implements InvocationHandler {
private Object object;
//为谁进行代理就把谁作为参数传递过来(有参构造函数传递) 比如这里给userDaoImpl代理 传入的参数就是userDaoImpl对象
public UserDaoProxy(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法执行之前...."+method.getName()+" 参数:"+ Arrays.toString(args));
Object result=method.invoke(object,args);
System.out.println("方法执行之后....");
return result;
}
}
2. CGLIB动态代理—无接口情况
被代理类:
package cn.edu.xd.dao;
public class UserDaoImpl{
public int add(int a, int b) {
System.out.println("add()方法执行...");
return a+b;
}
public void printMsg(String msg) {
System.out.println("update()方法执行...");
System.out.println("msg:"+msg);
}
}
代理类:
package cn.edu.xd.dao;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import java.util.Arrays;
public class UserDaoImplInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("方法执行之前...."+method.getName()+" 参数:"+ Arrays.toString(objects));
Object result=methodProxy.invokeSuper(o,objects);
System.out.println("方法执行之后....");
return result;
}
}
使用:
package cn.edu.xd.proxy;
import cn.edu.xd.dao.UserDaoImpl;
import cn.edu.xd.dao.UserDaoImplInterceptor;
import net.sf.cglib.proxy.Enhancer;
public class CglibProxy {
public static void main(String[] args) {
UserDaoImpl userDao= (UserDaoImpl) Enhancer.create(UserDaoImpl.class,new UserDaoImplInterceptor());
int result=userDao.add(1,2);
System.out.println("result:"+result);
userDao.printMsg("hello");
}
}
连接点
类里面哪些方法可以被增强,这些可以被增强的方法称为连接点
切入点
实际被真正增强的方法称为切入点
通知
实际增强的逻辑部分称为通知
通知分为5种:前置通知,后置通知,环绕通知,异常通知,最终通知
切面
是一个动作,把通知应用到切入点的过程
切入点表达式
确定哪个切入点,切入点表达式作用:知道对哪个类里面的哪个方法进行增强
语法结构: execution([权限修饰符] [返回类型] [类全路径] [[方法名称]([参数列表])
权限修饰符可以省略,其他的不能省略
eg1: 对 cn.edu.xd.dao.UserDao 类里面的 add 进行增强(…表示参数列表)
execution(* cn.edu.xd.dao.UserDao.add(..))
eg2: 对 cn.edu.xd.dao.UserDao类里面的所有的方法进行增强
execution(* cn.edu.xd.dao.UserDao.*(..))
eg3:对 cn.edu.xd.dao包里面所有类,类里面所有方法进行增强
execution(* cn.edu.xd.dao.*.*(..))
创建被代理类(需要被增强的类),加上注解@Component
package cn.edu.xd.bean;
import org.springframework.stereotype.Component;
@Component
public class User {
public void add(){
System.out.println("add()...");
}
}
创建代理类(增强类),加上注解@Component和@Aspect
package cn.edu.xd.proxy;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class UserProxy {
//前置通知
@Before(value = "execution(* cn.edu.xd.bean.User.add(..))")
public void before(){
System.out.println("before()...");
}
//最终通知 即使出现异常也会执行
@After(value = "execution(* cn.edu.xd.bean.User.add(..))")
public void after(){
System.out.println("after()...");
}
//后置通知(返回通知) 出现异常不会执行
@AfterReturning(value = "execution(* cn.edu.xd.bean.User.add(..))")
public void afterReturning(){
System.out.println("afterReturning()...");
}
//异常通知 出现异常时会执行
@AfterThrowing(value = "execution(* cn.edu.xd.bean.User.add(..))")
public void afterThrowing(){
System.out.println("afterThrowing()...");
}
//环绕通知
@Around(value = "execution(* cn.edu.xd.bean.User.add(..))")
public void around(ProceedingJoinPoint point) throws Throwable {
System.out.println("环绕之前");
point.proceed();
System.out.println("环绕之后");
}
}
为了使异常通知出现,修改代码如下:
public void add(){
int a=10/0;//设置除零错误
System.out.println("add()...");
}
相同的切入点抽取
上面代码中的value = "execution(* cn.edu.xd.bean.User.add(..))"
出现了多次,可以进行如下提取:
@Pointcut(value = "execution(* cn.edu.xd.bean.User.add(..))")
public void pointCommon(){
}
//前置通知
@Before(value = "pointCommon()")
public void before(){
System.out.println("before()...");
}
设置多个增强类的增强顺序
与前面的增强类UserProxy类似,现在又加入一个增强类UserProxy2,代码如下:
package cn.edu.xd.proxy;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component
@Aspect
@Order(1)
public class UserProxy {
@Pointcut(value = "execution(* cn.edu.xd.bean.User.add(..))")
public void pointCommon(){
}
//前置通知
@Before(value = "pointCommon()")
public void before(){
System.out.println("before()...");
}
//最终通知 即使出现异常也会执行
@After(value = "execution(* cn.edu.xd.bean.User.add(..))")
public void after(){
System.out.println("after()...");
}
//后置通知(返回通知) 出现异常不会执行
@AfterReturning(value = "execution(* cn.edu.xd.bean.User.add(..))")
public void afterReturning(){
System.out.println("afterReturning()...");
}
//异常通知 出现异常时会执行
@AfterThrowing(value = "execution(* cn.edu.xd.bean.User.add(..))")
public void afterThrowing(){
System.out.println("afterThrowing()...");
}
//环绕通知
@Around(value = "execution(* cn.edu.xd.bean.User.add(..))")
public void around(ProceedingJoinPoint point) throws Throwable {
System.out.println("环绕之前");
point.proceed();
System.out.println("环绕之后");
}
}
可以使用注解@Order(数字)的方式指定增强顺序,数字从0开始,数字越小,优先级越高
完全注解开发
package cn.edu.xd.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration //表示是配置类 相当于bean.xml
@ComponentScan(basePackages = {"cn.edu.xd"}) //表示对哪些包进行扫描
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class SpringConfig {
}
@Test
public void test1(){
ApplicationContext context
= new AnnotationConfigApplicationContext(SpringConfig.class);
User user = context.getBean("user", User.class);
user.add();
}
<bean id="user" class="cn.edu.xd.bean.User"/>
<bean id="userProxy" class="cn.edu.xd.proxy.UserProxy"/>
<aop:config>
<aop:pointcut id="p" expression="execution(* cn.edu.xd.bean.User.add(..))"/>
<aop:aspect ref="userProxy">
<aop:before method="before" pointcut-ref="p"/>
<aop:after method="after" pointcut-ref="p"/>
<aop:after-returning method="afterReturning" pointcut-ref="p"/>
<aop:around method="around" pointcut-ref="p"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="p"/>
aop:aspect>
aop:config>