目录
代理的基础概念
示例1:静态代理(场景:客户通过中介租房东的房子)
示例2:JDK动态代理实现房东、中介出租房屋
示例3:CGLib动态代理实现房东出租房屋
示例4:观察Spring IOC容器中代理对象详情
代理的基础概念
AOP是基于代理模式实现切点方法的动态扩展。当切点目标类实现了接口,AOP通过JDK自带的动态代理扩展被代理对象方法的功能;当切点目标类未实现接口,Spring 通过CGLib组件实现扩展被代理对象方法功能。
代理模式的核心是创建一个代理对象,代理对象内部包含封装了被代理对象,最终通过执行被代理对象的方法达到动态扩展方法的功能,代理模式分为静态代理和动态代理。
示例1:静态代理(场景:客户通过中介租房东的房子)
示意图如下:
EstateAgent(中介)和Landord(房东)都实现租房接口,Customer(客户)通过中介实现租房子,代码由以下各类组成:
1、接口(房屋出租接口)
package com.text.pattern;
//房屋出租接口
public interface RentalHouse {void rental();
}
2、房东类-实现租房接口
package com.text.pattern;
//房东类
public class Landlord implements RentalHouse{@Overridepublic void rental() {System.out.println("xxx栋xxx房屋出租");}
}
3、中介类--实现租房接口
package com.text.pattern;
//房产中介
public class EstateAgent implements RentalHouse{private Landlord landlord;//被代理对象public EstateAgent(Landlord landlord) {this.landlord = landlord;}@Overridepublic void rental() {System.out.println("中介收取客户中介费");this.landlord.rental();}
}
4、客户类-测试
package com.text.pattern;
//测试类
public class Customer {public static void main(String[] args) {System.out.println("客户找中介租房子");new EstateAgent(new Landlord()).rental();}
}
5、运行结果:
从运行结果中可以看出,房屋出租方法被扩展了中介收取客户手续费的功能。
静态代理的劣势:如果需要对很多目标类方法进行扩展,就需要额外编写很多的代理类,通过动态代理可以实现一个代理类对一批目标方法进行扩展,也就实现了AOP
示例2:JDK动态代理实现房东、中介出租房屋
1、代理执行hander(ProxyInvocationHandler)
package com.text.pattern;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class ProxyInvocationHandler implements InvocationHandler {private Object target;//被代理对象public ProxyInvocationHandler(Object target) {this.target = target;}/*** @param proxy 代理对象* @param method 被代理对象的方法* @param args 被代理对象方法的参数* @return* @throws Throwable*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("目标类方法执行之前功能扩展...");Object ret = method.invoke(target, args);System.out.println("目标类方法执行之后功能扩展...");return ret;}
}
2、被代理对象实现的接口
package com.text.pattern;
//房屋出租接口
public interface RentalHouse {void rental();
}
3、被代理对象,房东和中介都可以被代理
package com.text.pattern;
//房东类
public class Landlord implements RentalHouse{@Overridepublic void rental() {System.out.println("房东出租xxx栋xxx房屋");}
}
package com.text.pattern;
//房产中介
public class EstateAgent implements RentalHouse{@Overridepublic void rental() {System.out.println("中介出租xxx栋xxx房屋,并收取中介费");}
}
4、测试类 生成代理对象,房东的代理,中介的代理
package com.text.pattern;import java.lang.reflect.Proxy;//测试类
public class Customer {public static void main(String[] args) {RentalHouse landlord = new Landlord();//被代理对象RentalHouse proxyObj = (RentalHouse)Proxy.newProxyInstance(landlord.getClass().getClassLoader(),landlord.getClass().getInterfaces(), new ProxyInvocationHandler(landlord));proxyObj.rental();landlord = new EstateAgent();proxyObj = (RentalHouse)Proxy.newProxyInstance(landlord.getClass().getClassLoader(),landlord.getClass().getInterfaces(), new ProxyInvocationHandler(landlord));proxyObj.rental();}
}
5、运行结果:
从运行结果可以看出,房东和中介都实现了租房的接口,并且都被代理,他们分别在租房的同时都实现了各自方法的扩展,即一个代理类(ProxyInvocationHandler)实现了对多个目标方法的动态扩展。
示例3:CGLib动态代理实现房东出租房屋
如果房东类没有实现接口,Spring 采用CGlib组件实现AOP功能
1、目标类(房东)
package com.text.pattern;
//房东类
public class Landlord2{public void rental() {System.out.println("房东2出租xxx栋xxx房屋");}
}
2、 代理工厂类(CglibProxyFactory)
package com.text.pattern;import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class CglibProxyFactory implements MethodInterceptor {private Object target;//被代理的对象public CglibProxyFactory(Object target) {super();this.target = target;}//创建代理对象public Object getProxyInstance() {Enhancer en = new Enhancer();//父类en.setSuperclass(target.getClass());en.setCallback(this);//创建子类代理对象return en.create();}@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)throws Throwable {System.out.println("目标类方法执行之前功能扩展...");Object result = proxy.invokeSuper(obj, args);System.out.println("目标类方法执行之后功能扩展...");return result;}
}
3、测试类:
package com.text.pattern;//测试类
public class Customer {public static void main(String[] args) {Landlord2 landlord2 = new Landlord2();//被代理对象CglibProxyFactory proxyFactory = new CglibProxyFactory(landlord2);//生成代理对象Landlord2 proxyObj = (Landlord2)proxyFactory.getProxyInstance();proxyObj.rental();}
}
4、运行结果:
从运行结果看出,代理对象对房东2出租方法实现了功能扩展。
示例4:观察Spring IOC容器中代理对象详情
代码如下:
1、学生DAO接口、DAO实现类、Service接口、Service实现类、Controller类
package com.text.dao;public interface StudentDao {void getById(String id) throws Exception;
}
package com.text.dao.impl;import com.text.dao.StudentDao;
import org.springframework.stereotype.Repository;@Repository
public class StudentDaoImpl implements StudentDao {@Overridepublic void getById(String id) throws Exception {Thread.sleep(1000);System.out.println("查询学生id=" + id + "的信息");}
}
package com.text.service;import com.text.entity.Student;public interface StudentService {public void save(Student student);public void deleteById(String id);public void updateById(String id) throws Exception;public Student searchById(String id) throws Exception;
}
package com.text.service.impl;import com.text.dao.StudentDao;
import com.text.entity.Course;
import com.text.entity.Student;
import com.text.service.StudentService;
import org.springframework.stereotype.Service;import javax.annotation.Resource;@Service
public class StudentServiceImpl implements StudentService {@Resourceprivate StudentDao studentDao;public StudentDao getStudentDao() {return studentDao;}public void setStudentDao(StudentDao studentDao) {this.studentDao = studentDao;}@Overridepublic void save(Student student) {System.out.println(student + "正在被保存...");}@Overridepublic void deleteById(String id) {System.out.println("学生id=" + id + "的记录已被删除...");}@Overridepublic void updateById(String id) throws Exception{System.out.println("学生id=" + id + "的记录正在被修改...");throw new Exception("修改学生信息出异常");}@Overridepublic Student searchById(String id) throws Exception {System.out.println("已查询到学生id=" + id + "的记录...");Student student = new Student("张三",20,new Course("计算机"));return student;}
}
package com.text.controller;import com.text.entity.Student;
import com.text.service.StudentService;
import org.springframework.stereotype.Controller;import javax.annotation.Resource;@Controller
public class StudentController {@Resourceprivate StudentService studentService;public StudentService getStudentService() {return studentService;}public void setStudentService(StudentService studentService) {this.studentService = studentService;}public Student searchById(String id) throws Exception {return this.studentService.searchById(id);}}
2、切面类 ,实现对com.text包及子包以“DaoImpl”结尾的类的所有方法和com.text包及子包以“Controller”结尾的类的所有方法的环绕通知
package com.text.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
import java.util.Date;/*** 定义方法切面类*/
@EnableAspectJAutoProxy //开启AspectJ的注解方式
@Component
@Aspect //标识为切面类
public class MethodAspect {//配置环绕通知@Around("execution(public * com.text..*DaoImpl.*(..)) || execution(public * com.text..*Controller.*(..)) ")public void countMethodInvokeTime(ProceedingJoinPoint proceedingJoinPoint) {System.out.println("目标方法执行之前记录初始时间...");Date startTime = new Date();try {proceedingJoinPoint.proceed();//执行目标方法 即:StudentDaoImpl.getById方法System.out.println("目标方法执行之后记录结束时间...");String methodName = proceedingJoinPoint.getTarget().getClass().getName() + "." +proceedingJoinPoint.getSignature().getName();Date endTime = new Date();System.out.println(methodName + "方法执行总时长为:" + (endTime.getTime() - startTime.getTime()) + "毫秒");} catch (Throwable throwable) {throwable.printStackTrace();}}
}
3、测试类
package com.text;import com.text.controller.StudentController;
import com.text.dao.StudentDao;
import com.text.service.impl.StudentServiceImpl;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Application {public static void main(String[] args) throws Exception {ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");//jdk动态代理对象StudentDao studentDao = context.getBean("studentDaoImpl", StudentDao.class);//代理对象Object target = ((Advised)studentDao).getTargetSource().getTarget();//方式1:获取被代理的原始对象Object singletonTarget = AopProxyUtils.getSingletonTarget(studentDao);//方式2:获取被代理的原始对象StudentServiceImpl studentService = context.getBean("studentServiceImpl", StudentServiceImpl.class);System.out.println("2种方式获取的被代理对象是否一致:" + (target == singletonTarget));//CGLib代理对象StudentController studentController = context.getBean("studentController", StudentController.class);//controller代理对象studentController.searchById("1");Object controllerTarget = AopProxyUtils.getSingletonTarget(studentController);//获取被代理的原始对象}
}
4、运行结果及分析
5、程序Debug过程中的对象详情
从Debug信息可以看出:
- excution表达式包含的类,通过ApplicationContext.getBean方法获取的对象都是代理对象(studentDao和studentController对象),其中studentDao 实现了接口,所以是jdk的动态代理对象,studentController没有实现接口,是CGLib组件生成的代理对象。没有被excution表达式包含的类,如studentService对象,ApplicationContext.getBean方法获取的对象就是原始类型的对象
- 通过Advised.getTargetSource().getTarget()和AopProxyUtils.getSingletonTarget都可以获取被代理的目标对象,从程序看出,被代理的目标对象都是原始类型,并且被代理对象是同一个,内存地址都相同