重生之我在CSDN学java 第七天(IOC和AOP)
一、 IOC(Inversion of Control)
IOC:inverse of controll,控制翻转
DI: dependency injection,依赖注入,IOC的另一种叫法,更能体现这个模块的功能
spring-beans 和 spring-context 是 Spring Framework 的 IOC 容器的根本。 BeanFactory 接口提供了一种能够管理任何类型对象的高级配置机制。ApplicationContext 是 BeanFactory 的一个子接口。
ApplicationContext 使得与Spring 的AOP集成变得更简单,添加了资源处理(国际化中使用)、事件发布,以及特定于应用程序层的上下文,例如 WebApplicationContext
简而言之,BeanFactory提供了配置框架和基本功能,而ApplicationContext 添加了更多企业应用功能。ApplicationContext完整扩展了BeanFactory。
1. 核心概念
使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到外部, 此思想称为控制反转
Spring技术对Ioc思想进行了实现
Spring提供了一个容器,称为Ioc容器,用来充当Ioc思想中的“外部”
Ioc容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在Ioc容器中统称为Bean
2. 入门案例
创建一个spring的maven 项目
导spring上下文包
<!--spring-context:IOC必选,spring上下文包 -->
<!--依赖传递导入:commons-logging、spring-core、spring-beans、springexpression、spring-apo-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework-version}</version>
</dependency>
业务层实现 (依赖dao对象运行)
public class UserServiceImpl implements UserService {
private UserDao userDao;
@Override
public void save() {
userDao.save();
}
}
数据层实现
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("---UserDaoImpl---save");
}
}
创建spring的配置文件Bean
Bean配置文件
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="org.lwf.dao.impl.UserDaoImpl"/>
<bean id="userService" class="org.lwf.service.impl.UserServiceImpl"/>
</beans>
创建启动类
public class App {
public static void main(String[] args) {
//获取IOC容器
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取bean
UserDao userDao = (UserDao)context.getBean("userDao");
userDao.save();
//获取bean
// UserService userService = (UserService)context.getBean("userService");
// userService.save();
}
}
注意:充分解耦
使用Ioc容器管理bean(Ioc)
在Ioc容器内将有依赖关系的bean进行关系绑定(DI)
最终效果,使用对象时不仅可以直接从Ioc容器中获取,并且获取到的bean已经绑定了所有的依赖关系
IO 提供依赖对象对应的方法 setXx()
public class UserServiceImpl implements UserService {
private UserDao userDao;
@Override
public void save() {
userDao.save();
System.out.println("---UserServiceImpl---save");
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="org.lwf.dao.impl.UserDaoImpl"/>
<bean id="userService" class="org.lwf.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
</beans>
3. 依赖注入
为了缩减XML的体积,以及把配置元数据和代码放在一起,Spring2.5开始支持基于注解的配置,总体的思路是让Spring扫描类路径下的Class,
通过反射检查是否有特定注解,如果类上存在@Service一类的注解则实例化该类为bean,依赖注入由自动注册的相关BeanPostProcessor根据注解来完成。
1. 构造函数注入
构造器注入可以根据参数索引注入、参数类型注入或Spring3支持的参数名注入。参数名注入是有限制的,需要在编译时打开调试模式(即在编译时使用“javac –g:vars”选项,在class文件中生成变量调试信息,默认是不包含变量调试信息的),否则获取不到参数名字,或在构造器上使用注解@ConstructorProperties来指定参数名。
创建有参构造
public class UserServiceImpl implements UserService {
private UserDao userDao;
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void save() {
userDao.save();
}
}
配置Spring容器调用构造函数进行注入
<bean id="userDao" class="org.lwf.dao.impl.UserDaoImpl"/>
<bean id="userService" class="org.lwf.service.impl.UserServiceImpl">
<constructor-arg name="userDao" ref="userDao"></constructor-arg>
</bean>
2. Setter注入
属性注入是指通过调用bean的属性设置方法来注入依赖项,在bean中需要存在对应的set方法。
创建UserServiceImpl类,并在其中添加setUserDao (set)方法
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao){
this.userDao=userDao;
}
@Override
public void save() {
userDao.save();
}
}
配置Spring容器调用set方法进行注入
<bean id="userService" class="org.lwf.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
3. 注解注入
也可以使用注解配置依赖注入,注解注入是不需要提供Setter方法的,Spring通过泛型修改依赖项的访问控制,然后直接设置属。但是需要通过配置项 <context:annotation-config /> 或者**<context:component-scan base-package=“com”>** 来加载对应的注解处理器。
属性注入
@Service("userService")
public class UserService implements IUserService {
@Autowired
private IUserDAO userDAO = null;
}
4. Bean 基础配置
用于配置对象交由Spring 来创建。
默认情况下它调用的是类中的无参构造函数,如果没有无参构造函数则不能创建成功。
格式
<beans>
<bean/>
<bean></bean>
</beans>
实例
<bean id="userDao" class="org.lwf.dao.impl.UserDaoImpl"/>
配置解析
id: bean的id,使用容器可以通过id值获取对应的bean,在一个容器中id 是唯一的。
class:bean的类型,即配置的bean的路径类名
bean实例化对象的三种方式
1. 构造方法
代码实现
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("---UserDaoImpl---save");
}
}
配置
<bean id="userDao" class="org.lwf.dao.impl.UserDaoImpl"/>
2. 静态工厂
代码实现
public class UserDaoFactory {
public static UserDao getUserDao() {
return new UserDaoImpl();
}
}
配置
<bean id="userDaoFactory" factory-method="getUserDao" class="org.lwf.factory.UserDaoFactory"/>
3. 实例工厂
代码实现
public class UserDaoFactory {
public UserDao getUserDao() {
return new UserDaoImpl();
}
}
配置
<bean id="userDaoFactory" class="org.lwf.factory.UserDaoFactory"/>
<bean id="userDao" factory-method="getUserDao" factory-bean="userDaoFactory">
二、 AOP(Aspect Oriented Programming)
面向切面编程(AOP)通过提供另外一种思考程序结构的途经来弥补面向对象编程(OOP)的不足。在OOP中模块化的关键单元是类(classes),而在AOP中模块化的单元则是切面。切面能对关注点进行模块化,例如横切多个类型和对象的事务管理。(在AOP术语中通常称作横切(crosscutting))
1. 核心概念
切面(Aspect)
一个关注点的模块化,是通知和切点的结合。通知和切点共同定义了切面的全部内容——它是什么,在何时和何处完成其功能。
连接点(Joinpoint)
连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。因为Spring基于代理实现AOP,所以只支持方法连接点。
通知(Advice)
在切面的某个特定的连接点上执行的动作。Spring切面可以应用5种类型的通知:
1. 前置通知(Before):在目标方法被调用之前调用通知功能;
2. 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
3. 返回通知(After-returning):在目标方法成功执行之后调用通知;
4. 异常通知(After-throwing):在目标方法抛出异常后调用通知;
5. 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。
6. 引入通知(Introduction):用来给一个类型声明额外的方法或属性。Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象。可以在无需修改现有的类的情况下,让它们具有新的行为和状态。
切入点(Pointcut)
切入点是匹配连接点的表达式。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行。一个切面并不需要通知应用的所有连接点。切点有助于缩小切面所通知的连接点的范围。
目标对象(Target Object)
被一个或者多个切面所通知的对象。也被称做被通知(advised)对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个被代理(proxied)对象。
AOP代理(AOP Proxy)
AOP框架创建的对象,用来实现切面(例如通知方法执行等等)。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。
织入(Weaving)
织入是把切面应用到目标对象并创建新的代理对象的过程。切面在指定的连接点被织入到目标对象中。在目标对象的生命周期里有多个点可以进行织入:
1. 编译期:切面在目标类编译时被织入。这种方式需要特殊的编译器。AspectJ的织入编译器就是以这种方式织入切面的。
2. 类加载期:切面在目标类加载到JVM时被织入。这种方式需要特殊的类加载器(ClassLoader),它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ 5的加载时织入(loadtimeweaving,LTW)就支持以这种方式织入切面。Spring可以通过spring-instrument使用AspectJ 的加载时植入。
3. 运行期:切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。Spring AOP默认就是以这种方式织入切面的。
2. Spring AOP
1. Spring提供了4种类型的AOP支持
经典Spring AOP
基于XML配置,使用ProxyFactoryBean生成代理对象,相对纯POJO切面和基于注解的AOP,Spring经典的AOP看起来比较笨重和过于复杂。
纯POJO切面
借助Spring的aop命名空间,我们可以将纯POJO转换为切面。实际上,这些POJO只是提供了满足切点条件时所要调用的方法。遗憾的是,这种技术需要XML配置,但这的确是声明式地将对象转换为切面的简便方式。
@AspectJ注解驱动的切面
Spring借鉴了AspectJ的切面,以提供注解驱动的AOP。本质上,它依然是Spring基于代理的AOP,但是编程模型几乎与编写成熟的AspectJ注解切面完全一致。这种AOP风格的好处在于能够不使用XML来完成功能。
注入式AspectJ切面
如果AOP的切点要求不只是方法(如构造器或属性拦截),那么需要考虑使用AspectJ来实现切面。前三种都是Spring AOP实现的变体。Spring AOP构建在动态代理基础之上,因此,Spring对AOP的支持局限于方法拦截。
2. 工程依赖
Spring AOP是基于IOC的,在IOC的工程依赖基础上需要导入AOP相关的包,如果是Maven工程,则需要加入以下依赖配置:
<!--spring-aop:必选,spring-aop包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!--spring-aspects:必选,spring非经典AOP的依赖 -->
<!--依赖传递导入:aspectjweaver-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${org.springframework-version}</version>
</dependency>
3. 配置文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
注意:
1. XML风格对现有的Spring用户来说更加习惯,而且从配置中可以清晰的表明在系统中存在那些切面。但是有如下的缺点:
它不能完全将需求实现的地方封装到一个位置。
XML风格同@AspectJ风格所能表达的内容相比有更多的限制。
2. @AspectJ风格支持其它的实例模型以及更丰富的连接点组合。它具有将切面保持为一 个模块单元的优点。
还有一个优点是@AspectJ切面能被Spring AOP和AspectJ两者都理解,可以迁移到AspectJ的AOP
3. AOP的实现
1. 经典Spring AOP(了解)
完全基于XML进行配置,通知需要实现对应的接口
1、 获取上下文
ApplicationContext applicationContext = new
ClassPathXmlApplicationContext("com/demo/spring/sample/step02/proxyfactorybean/a
pplicationContext.xml");
IUserService userService = (IUserService)
applicationContext.getBean("userService");
userService.create();
2、applicationContext.xml
<?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:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd" >
<bean id="userServiceTarget"
class="com.demo.spring.sample.step02.proxyfactorybean.service.UserService" />
<!-- 前置通知 -->
<bean id="myMethodBeforeAdvice"
class="com.demo....step02.proxyfactorybean.advice.MyMethodBeforeAdvice"/>
<!-- ************ 对接口进行代理 ************ -->
<!--将每一个连接点都当做切点(拦截每一个方法) -->
<bean id="userService"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!--需要代理的接口 -->
<property name="proxyInterfaces">
<value>com.demo.spring.sample.step02.proxyfactorybean.service.IUserService
</value>
</property>
<!--目标对象 -->
<property name="target" >
<ref bean="userServiceTarget" />
</property>
<!--被应用的通知 -->
<property name="interceptorNames">
<list>
<value>myMethodBeforeAdvice</value>
</list>
</property>
</bean>
</beans>
3、 定义通知
/**
* 前置通知
*/
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
private static Logger logger =
LogManager.getLogger(MyAfterReturningAdvice.class);
public void before(Method method, Object[] args, Object target) throws
Throwable{
logger.debug("前置通知:方法=" + method.getName() + ",参数数量=" +
args.length + ",目标对象的类=" + target.getClass());
}
}
2. 纯POJO切面
通知不需要实现相关接口,只是一个简单的java类,切点和切面的定义在XML中。
由于spring在解析切点的定义时(org.springframework.aop.aspectj.AspectJExpressionPointcut),使用的是aspectj的工具类,所以需要导入aspectjweaver.jar。
如果被代理的目标对象实现了至少一个接口,则会使用JDK动态代理。所有该目标类型实现的接口都将被代理。 若该目标对象没有实现任何接口,则创建一个CGLIB代理。也可以通过proxy-target-class调整。cglib的相关类文件打包在了spring-core-*.RELEASE.jar中
POJO: Plain Ordinary Java Object)简单的Java对象,实际就是普通JavaBean
1、获取上下文
ApplicationContext applicationContext = new
ClassPathXmlApplicationContext("com/demo/spring/sample/step02/pojo/applicationCo
ntext.xml");
UserService userService = (UserService)
applicationContext.getBean("userService");
userService.create(12);
IRoleService roleService = (IRoleService) userService;
roleService.delete(11);
2、applicationContext.xml
<?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:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd" >
<!-- ************ 纯POJO切面 ************ -->
<bean id="traceLog" class="com.demo.spring.sample.step02.pojo.TraceLog" />
<aop:config proxy-target-class="false">
<!-- 声明一个切面 -->
<aop:aspect id="log" ref="traceLog">
<!-- 声明一个切入点 -->
<aop:pointcut id="addAllMethod"
expression="execution(* com.demo.....step02.pojo.UserService.*(..))" />
<!-- 前置通知 -->
<aop:before method="before" pointcut-ref="addAllMethod" />
</aop:aspect>
</aop:config>
<bean id="userService" class="com.demo.spring.sample.step02.pojo.UserService" /></beans>
切点定义表达式
execution(<修饰符模式>? <返回类型模式> <方法名模式>(<参数模式>) <异常模式>?)
1. 各部分说明:
修饰符模式: 可选,如public、protected;
返回值类型: 可以是任何类型模式;
方法名: 可以使用“*”进行模式匹配;
参数列表: “()”表示方法没有任何参数;
异常列表: 可选,以“throws 异常全限定名列表”声明,异常全限定名列表如有多个
以“,”分割,如throws java.lang.IllegalArgumentExceptionjava.lang.ArrayIndexOutOfBoundsException。
注意:除了返回类型模式、方法名模式和参数模式外,其它项都是可选的。
2. 类型匹配的通配符: * :匹配任何数量字符; .. :匹配任何数量字符重复,如在的类
型模式中匹配任何数量子包;而在方法参数模式中匹配任何数量参数。 + :匹配指定类
型的子类型;仅能作为后缀放在类型模式后边。
例如:
java.lang.String :匹配String类型;
java.*.String : 匹配java包下的任何“一级子包”下的String类型; 如匹配
java.lang.String,但不匹配java.lang.ss.String *
java.. :匹配java包及任何子包下的任何类型; 如匹配
java.lang.annotation.Annotation 、java.lang.String、
java.lang.*ing 匹配任何java.lang包下的以ing结尾的类型;
java.lang.Number+ 匹配java.lang包下的任何Number的子类型;如匹配
java.lang.Integer,也匹配java.math.BigInteger
3、 TraceLog.java
package com.demo.spring.sample.step02.pojo;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
/**
* 通知类 POJO
*/
public class TraceLog {
private static Logger logger = LogManager.getLogger(TraceLog.class);
public void before() {
logger.debug("前置通知");
}
public void after() {
logger.debug("后置通知");
}
public void afterReturning(Object retVal) {
logger.debug("后置通知:返回值=" + (retVal == null ? "" :
retVal.toString()));
}
public void afterThrowing(Exception ex) {
logger.debug("异常通知:异常信息=" + ex.toString());
}
public Object around(ProceedingJoinPoint pjp) throws Throwable {
logger.debug("环绕通知:代理目标=" + pjp.getTarget().getClass());
Object retVal = pjp.proceed();
return retVal;
}
}
3. @AspectJ切面
AspectJ 5使用了Java注解定义切面,Spring 使用了和AspectJ 5一样的注解,并使用AspectJ来做切入点解析和匹配。 但是,AOP在运行时仍旧是纯的Spring AOP,并不依赖于AspectJ的编译器或者织入器(weaver)。
启用@AspectJ注解定义aop:
1、通过在Spring的配置中引入下列元素来启用Spring对@AspectJ的支持:
<aop:aspectj-autoproxy/>
该配置告诉Spring容器,AOP要通过注解来定义,注解信息在加入@Aspect注解的类中
2、AOP并不知道切面的定义在哪些类里,所以需要让Spring容器创建包含切面定义类的实例,从而找到切面定义:
<bean id="traceLog" class="com.demo.spring.sample.step02.aspectj.TraceLog" />
当让也可以在配置类中同时加入 @Component 注解,由Spring的自动扫描类扫描AOP注解定义所在的类,然后注册到上下文。
1、 获取上下文
ApplicationContext applicationContext = new
ClassPathXmlApplicationContext("com/demo/spring/sample/step02/aspectj/applicatio
nContext.xml");
IUserService userService = (IUserService)
applicationContext.getBean("userService");
userService.create();
userService.create(10);
2、 切点定义类
package com.demo.spring.sample.step02.aspectj;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class TraceLog {
private static Logger logger = LogManager.getLogger(TraceLog.class);
/**
* 这个切点定义了spring包下的任意service实现类的任意方法,包结构可能是:
* com.demo.spring.user.service.impl
*/
@Pointcut("execution(* com.demo.spring..service.*.*(..))")
public void businessService() {
}
/**
* 使用已命名的切点,也可以使用内置的切入点表达式
*/
@Before("com.demo.spring.sample.step02.aspectj.TraceLog.businessService()")
public void before() {
System.out.println("----------------------------");
logger.debug("前置通知");
}
}
3、applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
<aop:aspectj-autoproxy />
<!-- 自动代理是指Spring会判断一个bean是否使用了一个或多个切面通知,并据此自动生成相应的代
理以拦截其方法调用,并且确保通知在需要时执行。 -->
<!-- Spring不会自动扫描切面定义类TraceLog,需要在Spring中注册为Bean才可以,或者启用组
件自动扫描,并且在切面定义类中使用@Component注解 -->
<bean id="traceLog" class="com.demo.spring.sample.step02.aspectj.TraceLog"/>
<bean id="userService" class="com.demo.spring.sample.step02.aspectj.service.impl.UserService"/>
</beans>
4. 代理模式
为其他对象提供一种代理以控制对这个对象的访问
1. 静态代理
静态代理是由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了
Client.java
/**
* 静态代理演示代码
*/
public class Client {
/**
* @param args
*/
public static void main(String[] args) {
IUserService userService = new ProxyUserServiceImpl(new
UserServiceImpl());
userService.create(101);
}
}
IUserService.java
/**
* 接口,由目标类和代理实现
*/
public interface IUserService {
public void create(int id);
}
}
ProxyUserServiceImpl.java
/**
* 代码类,拦截目标方法的调用
*/
public class ProxyUserServiceImpl implements IUserService {
private UserServiceImpl userService = null;
public ProxyUserServiceImpl(UserServiceImpl userService) {
this.userService = userService;
}
public void create(int id) {
if (id > 100) {
throw new RuntimeException();
}
userService.create(id);
}
}
UserServiceImpl.java
/**
* 目标类,被代理类
*/
public class UserServiceImpl implements IUserService {
public void create(int id) {
System.out.println("create");
}
}
2. 动态代理
动态代理是指在实现阶段不用关心代理类,代理类在运行阶段动态生成。
1. JDK动态代理
Client.java
package com.demo.spring.sample.step00.proxy.dynamic.jdk;
/**
* jdk动态代理,要求被代理类实现接口
*/
public class Client {
public static void main(String[] args) {
IUserService userService = (IUserService)
Factory.create(IUserService.class,
new UserServiceImpl());
userService.create(101);
}
}
Factory.java
import java.lang.reflect.Proxy;
/**
* 根据传入的接口class实例和对应的实现类实例生成代理对象
*/
public class Factory {
public static Object create(Class Superclass, Object target) {
Handler handler = new Handler(target);
/*
* 动态生成代理对象:
* Factory.class.getClassLoader():类加载器
* new Class[] { clazz }:接口对应的class
* handler:调用拦截器
*/
return Proxy.newProxyInstance(Factory.class.getClassLoader(), new
Class[]
{Superclass}, handler);
}
}
Handler.java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 调用拦截器
*/
public class Handler implements InvocationHandler {
private Object targetObject = null;
public Handler(Object targetObject) {
this.targetObject = targetObject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable {
System.out.println("-----------Handler--->方法调用前---------------------
");
if("create".equals(method.getName())){
int id=(Integer)args[0];
if (id > 100) {
throw new RuntimeException();
}
}
Object object = method.invoke(targetObject, args);
System.out.println("-----------Handler--->方法调用后---------------------
");
return object;
}
}
IUserService.java
/**
* 接口,由目标类实现
*/
public interface IUserService {
public void create(int id);
}
UserServiceImpl.java
/**
* 目标类,被代理类
*
*/
public class UserServiceImpl implements IUserService {
public void create(int id) {
System.out.println("create");
}
}
2. cglib代理
JDK动态代理要求被代理对象至少实现一个接口,如果被代理对象没有实现接口,则需要通过字节码工具生成一个代理类,cglib使用ASM生成代理类。
Client.java
/**
* cglib代理
*/
public class Client {
public static void main(String[] args) {
UserServiceImpl userService = (UserServiceImpl)
Factory.create(UserServiceImpl.class);
userService.create(10);
}
}
Factory.java
import org.springframework.cglib.proxy.Enhancer;
/**
* 根据传入的接口名称和对应的实现类实例生成代理对象
*/
public class Factory {
public static Object create(Class Superclass) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Superclass);
enhancer.setCallback(new Interceptor());
//动态生成代理对象
return enhancer.create();
}
}
Interceptor.java
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* 目标对象拦截器,实现MethodInterceptor
*/
public class Interceptor implements MethodInterceptor {
/**
* @param obj 由CGLib动态生成的代理类实例,
* @param method Method为上文中实体类所调用的被代理的方法引用
* @param params Object[]为参数值列表
* @param proxy 为生成的代理类对方法的代理引用。
* @return 从代理实例的方法调用返回的值。
* @throws Throwable
*/
public Object intercept(Object obj, Method method, Object[] params,
MethodProxy proxy) throws Throwable {
System.out.println("-----------Interceptor--->方法调用前------------------
---");
if ("create".equals(method.getName())) {
int id = (Integer) params[0];
if (id > 100) {
throw new RuntimeException();
}
}
Object result = proxy.invokeSuper(obj, params);
System.out.println("-----------Interceptor--->方法调用后------------------
---");
return result;
}
}
UserServiceImpl.java
/**
* 目标类,被代理类
*
*/
public class UserServiceImpl implements IUserService {
public void create(int id) {
System.out.println("create");
}
}