一,介绍代理模式:
1.代理模式 设计模式的一种,解决某一类问题的产生 分为:
静态代理
动态代理【JDK动态代理、CGLIB动态代理】
2.代理模式的角色
(1).抽象对象(抽象父类或接口):需要完成的功能
(2).被代理对象:隐藏起来的对象
(3).代理对象:暴露给其他人的对象,访问被代理对象通过代理对象进行访问
3.代理模式案例:我们找中介租房 1.抽象对象: 租房 2.被代理对象:房东 3.代理对象:中介(做了额外事情,包房东保护起来) 4.调用者:我们
4.代理模式的好处 :
(1).被代理对象可以专注完成自己的业务(房东安心的做自己的事情即可,不用管理其他的事情)
(2).保护了被代理对象(房东对象比较的安全)
(3).增强了代码的扩展性
5.静态代理缺点: .随着房东/被代理对像的增多,中介的压力就会越来越大,体现到代码上就是代码越来越臃肿
6,实现静态代理的步骤
(1), 创建抽象对象: Zufang接口
public interface Zufang {
/*
* 需要完成的事情
*/
void zufang();
}
(2)创建被代理对象Fangdong
public class Fangdong implements Zufang {
public void zufang() {
System.out.println("出租五环科技园C座c01");
}
}
(3)创建代理对象Zhongjie
public class Zhongjie implements Zufang {
//房东人:张三
private Fangdong fangdong = new Fangdong();
@Override
public void zufang() {
System.out.println("收房东的中介费");
fangdong.zufang();
System.out.println("收我们的中介费");
}
}
(4)创建调用者Women
public class Women { public static void main(String[] args) { //小王 Zhongjie zhongjie = new Zhongjie(); zhongjie.zufang(); }}
二,实现AOP:第一阶段的静态代理
1.创建接口UserService
void add() throw Exception;
void delet() throw Exception;
2.创建接口实现类UserServiceImpl
public void add() throw Exception{
check();
System.out.println("添加成功");
log();
}
public void check(){
System.out.println("权限检查");
}
public void log(){
System.out.println("日志记录");
}
3.编写测试方法
ApplicationContext app=new ClassPathXmlApplicationContext("applicationTest.xml");
UserService UserService=(UserService)app.getBean("UserService");
UserService.add();
4.存在问题:
第一阶段的静态代理,在UserServiceImpl实现类中业务代码严重的与横切性代码耦合在一起了。
解决办法:将横切性代码提取出来,与业务代码 分开存放。
三,实现AOP:第二阶段的静态代理
1.创建一个静态代理类:
public class UserServiceProxy implement UserService{
private UserServiceImpl usi;//静态代理,指明代理的是谁。
public UserServiceProxy(UserServiceImpl userServiceImpl){
this.usi=userServiceImpl;
}
public void add() throw Exception{
check();
usi.add();
log();
}
public void check(){
System.out.println("权限检查");
}
public void log(){
System.out.println("日志记录");
}
}
com.jr.proxy 包下创建创建UserServiceProxy类 实现跟UserServiceImpl类一样的接口
2.实现类UserServiceImpl代码改动
public void add() throw Exception{
System.out.println("添加成功");
}
3.存在问题:
第二阶段的静态代理,将UserServiceImpl实现类中的横切性代码提取出来 放到代理类里,在代理类里做整合。
那么一个service接口就要写一个代理类,如果Service接口特别多的话,程序中将多出非常多的代理类。
解决办法:需要一个代理类,能代理所有实现接口的实现类。这就需要下面的动态代理
四:AOP实现:第三阶段:动态代理实现AOP
1,动态代理简介:
(1).动态代理的分类:
JDK动态代理---代理拥有接口的实现类
cglib动态代理---代理拥有父类的子类
(2).动态代理:
底层是根据反射实现的.
只要给代理对象传递被代理对象,直接调用被代理对象中方法
(3).动态代理中动态含义:
代理类可以代理任意类型对象.(被代理对象必须实现了指定接口)
2,JDK动态代理实现AOP
创建动态代理类:
//使用动态代理时需要实现InvocationHandler,因为在调用者中通过接口调用方法时知道需要执行哪个方法
public class JDKProxy implements InvocationHandler {
private Object targetObject;//代理的目标对象
//产生代理对象:
public Object createProxyInstance(Object targetObject){
this.targetObject=targetObject;
/**
* 参数一 ClassLoader 类加载器
* 参数二: Interfaces 接口类型
* 参数三: InvocationHandler 代理对象
* */
return Proxy.newProxyInstance(
this.targetObject.getclass().getClassLoader(),
this.targetObject.getclass().getInterfaces(),
this);
}
/*
* 三个参数
* 1.proxy:就是代理对象本身
* 2.method:代理对象调用的方法,被封装为的对象。简单说 谁在运行调用,这个method就是谁。
* 3.args:代理对象调用方法时,传递的实际参数
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
check();
Object invoke = method.invoke(this.targetObject, args);
log();
return invoke;
}
public void check(){
System.out.println("权限检查");
}
public void log(){
System.out.println("日志记录");
}
}
底层代码实现
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) throws IllegalArgumentException{
// 浅克隆一份接口的class文件数组,数组是另一块内存空间;但数组里面的class还是同一个class,都是指向同一块内存地址
final Class<?>[] intfs = interfaces.clone();
// 查找或生成指定的代理类class文件,下面在说这个方法
Class<?> cl = getProxyClass0(loader, intfs);
// 获取代理class的有参构造方法(参数就是InvocationHandler接口)
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
// 此处判断构造方法是否是公开权限,如果不是则设置访问权限
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
// 反射实例化代理对象
return cons.newInstance(new Object[]{h});
}
创建测试类:
@Test
public void show2(){
//1.加载Spring的核心配置文件
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("ApplicationContext.xml");
JDKProxy jdkProxy=applicationContext.getBean("proxy",JDKProxy.class);
//给UserBiz接口创建临时代理对象userBiz
UserBiz userBiz=(UserBiz) jdkProxy.getTargetObj(new UserBizImpl());
//----invoke方法是核心代理逻辑思想,代理对象调用的所有方法都会触发该方法执行---
userBiz.add();
}
存在问题:
JDK动态代理的产生必须要实现对应的接口的,如果没有对应的接口,这个时候代理对象就没有办法产生。
3,CGLIB动态代理实现AOP
创建父类
public abstract class UserSuper {
public abstract void add();
}
创建子类
public class UserSub extends UserSuper{
public void add(){
System.out.println("添加成功!");
}
}
创建动态代理类
使用Enhancer类中的create方法 2.create方法的参数: Class:字节码 :它是用于指定被代理对象的字节码。 Callback:用于提供增强的代码, 它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。 此接口的实现类都是谁用谁写。我们一般写的都是该接口的子接口实现类:MethodInterceptor
public class CGLibProxy implements MethodInterceptor{ //拦截器
private Object targetObject;//代理的目标对象
//产生代理对象:
public Object createProxyInstance(Object targetObject){
this.targetObject=targetObject;
Enhancer en=new Enhancer();//该类用于生成子类的代理对象
en.setSuperclass(this.targetObject.class);//被代理对象的字节码。
en.setCallback(this); //指定回收对象
return en.create();
}
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
check();
Object o1 = methodProxy.invokeSuper(o, objects);
log();
return o1;
}
public void check(){
System.out.println("权限检查");
}
public void log(){
System.out.println("日志记录");
}
}
创建测试类
CGLBProxy cglbProxy=new CGLBProxy();
UserBiz userBiz=(UserBiz) cglbProxy.createProxyInstance(new UserBizImpl());
userBiz.upd();
存在问题:
横切行代码最好还是存放在一个单独的类里面去,然后借助动态代理,在作用在代理类对象上。
Spring提供了两种切面声明方式,实际工作中我们可以选择其中一种: A) 基于XML配置方式声明切面 B) 基于注解方式声明切面
五:第四阶段:基于XML配置方式声明切面
1.添加依赖
<dependencies>
<!--进行junit单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--依赖于commons-logging日志管理 -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!--提供了框架的基本组成部分,包括IOC 和 DI-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.1.6.RELEASE</version>
</dependency>
<!-- 提供了BeanFactory-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.1.6.RELEASE</version>
</dependency>
<!--上下文配置对象,提供一个框架式的对象访问方式-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.1.6.RELEASE</version>
</dependency>
<!--提供了强大的表达式语言-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.1.6.RELEASE</version>
</dependency>
<!-- ====注解式声明切面 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.1</version>
</dependency>
</dependencies>
2.定义切面类:
public class Advice {
public void check(){
System.out.println("权限检查");
}
public void log(){
System.out.println("日志记录");
}
}
3.编写业务代码:
public interface UserService {
void add() throws Exception;
void delete() throws Exception;
}
public class UserServiceImpl implements UserService {
public void add() throws Exception {
System.out.println("添加User成功");
}
public void delete() throws Exception {
System.out.println("删除User成功");
}
}
4.编写applicationContext.xml配置文件
===引入一个命名空间: aop
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
<!--创建业务实现类对象 -->
<bean id="usi" class="com.jr.aop.aspectj.UserServiceImpl"/>
<!--创建切面类对象 -->
<bean id="advice" class="com.jr.aop.aspectj.Advice"/>
<!-- <aop:config proxy-target-class="true">
如果这样配置则是强制使用CGLIB方式进行代理
不写或者设置为false默认使用 : jdk方式进行代理
不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。-->
<aop:config>
<aop:aspect ref="advice">
<!--定义切点: -->
<aop:pointcut id="addpointcut" expression="execution(* com.jr.aop.aspectj.UserServiceImpl.add*(..))"/>
<!--定义通知 -->
<aop:before method="check" pointcut-ref="addpointcut"/>
<aop:after method="log" pointcut="execution(* com.jr.aop.aspectj.UserServiceImpl.add*(..))"/>
</aop:aspect>
</aop:config>
5.编写测试代码:
@Test
public void test5() throws Exception {
ApplicationContext app=new ClassPathXmlApplicationContext("applicationAOP.xml");
UserService userService=(UserService)app.getBean("usi");
userService.add();
userService.delete();
}
运行结果: 权限检查 添加User成功 日志记录 删除User成功
六:第四阶段:基于注解方式声明切面
1.添加依赖
<dependencies>
<!--进行junit单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--依赖于commons-logging日志管理 -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!--提供了框架的基本组成部分,包括IOC 和 DI-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.1.6.RELEASE</version>
</dependency>
<!-- 提供了BeanFactory-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.1.6.RELEASE</version>
</dependency>
<!--上下文配置对象,提供一个框架式的对象访问方式-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.1.6.RELEASE</version>
</dependency>
<!--提供了强大的表达式语言-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.1.6.RELEASE</version>
</dependency>
<!-- ====注解式声明切面 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.1</version>
</dependency>
</dependencies>
2.定义切面类:
@Aspect
@Component
public class Advice {
@Before("execution(* com.jr.aop.aspectj.UserServiceImpl.add*(..))")
public void check(){
System.out.println("权限检查");
}
@After("execution(* com.jr.aop.aspectj.UserServiceImpl.add*(..))")
public void log(){
System.out.println("日志记录");
}
}
3.编写业务代码:
public interface UserService {
void add() throws Exception;
void delete() throws Exception;
}
@Service
public class UserServiceImpl implements UserService {
public void add() throws Exception {
System.out.println("添加User成功");
}
public void delete() throws Exception {
System.out.println("删除User成功");
}
}
4.编写applicationContext.xml配置文件
===引入两个命名空间:context 和 aop
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
<!-- 扫描指定路径,自动注入注解-->
<context:component-scan base-package="com.jr"/>
<!--自动为spring容器中那些配置@aspectJ切面的bean创建代理,
有一个proxy-target-class属性,默认为false 那么标准的JDK 基于接口的代理将起作用。
不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。-->
<aop:aspectj-autoproxy/>
5.编写测试代码:
@Test
public void test5() throws Exception {
ApplicationContext app=new ClassPathXmlApplicationContext("applicationAOP.xml");
UserService userService=(UserService)app.getBean("userServiceImpl");
userService.add();
userService.delete();
}
运行结果: 权限检查 添加User成功 日志记录 删除User成功