目录
代理的基础概念
示例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{
@Override
public 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;
}
@Override
public 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
*/
@Override
public 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{
@Override
public void rental() {
System.out.println("房东出租xxx栋xxx房屋");
}
}
package com.text.pattern;
//房产中介
public class EstateAgent implements RentalHouse{
@Override
public 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();
}
@Override
public 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 {
@Override
public 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 {
@Resource
private StudentDao studentDao;
public StudentDao getStudentDao() {
return studentDao;
}
public void setStudentDao(StudentDao studentDao) {
this.studentDao = studentDao;
}
@Override
public void save(Student student) {
System.out.println(student + "正在被保存...");
}
@Override
public void deleteById(String id) {
System.out.println("学生id=" + id + "的记录已被删除...");
}
@Override
public void updateById(String id) throws Exception{
System.out.println("学生id=" + id + "的记录正在被修改...");
throw new Exception("修改学生信息出异常");
}
@Override
public 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 {
@Resource
private 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都可以获取被代理的目标对象,从程序看出,被代理的目标对象都是原始类型,并且被代理对象是同一个,内存地址都相同