Spring框架(IOC和AOP详解)

重生之我在CSDN学java 第七天(IOC和AOP)

一、 IOC(Inversion of Control)

IOC:inverse of controll,控制翻转 
DI: dependency injection,依赖注入,IOC的另一种叫法,更能体现这个模块的功能

spring-beansspring-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");
	}
}
  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值