此处我们使用的是一个演员的例子:在很久以前,演员和剧组都是直接见面联系的。没有中间人环节。而随着时间的推移,产生了一个新兴职业:经纪人(中间人),这个时候剧组再想找演员就需要通过经纪人来找了。
public interface IActor {
/**
* 基本演出
* @param money
*/
public void basicAct(float money);
/**
* 危险演出
* @param money
*/
public void dangerAct(float money);
}
/**
* 一个演员
*/
//实现了接口,就表示具有接口中的方法实现。即:符合经纪公司的要求
public class Actor implements IActor{
public void basicAct(float money){
System.out.println("拿到钱,开始基本的表演:"+money);
}
public void dangerAct(float money){
System.out.println("拿到钱,开始危险的表演:"+money);
}
}
public class Client {
public static void main(String[] args) {
//一个剧组找演员:
final Actor actor = new Actor();//直接
/**
* 代理:
* 间接。
* 获取代理对象:
* 要求:
* 被代理类最少实现一个接口
* 创建的方式
* Proxy.newProxyInstance(三个参数)
* 参数含义:
* ClassLoader:和被代理对象使用相同的类加载器。
* Interfaces:和被代理对象具有相同的行为。实现相同的接口。
* InvocationHandler:如何代理。
*
策略模式:使用场景是:
*
数据有了,目的明确。
*
如何达成目标,就是策略。
*
*/
IActor proxyActor = (IActor) Proxy.newProxyInstance(
actor.getClass().getClassLoader(),
actor.getClass().getInterfaces(),
new InvocationHandler() {
/**
* 执行被代理对象的任何方法,都会经过该方法。
* 此方法有拦截的功能。
*
* 参数:
* proxy:代理对象的引用。不一定每次都用得到
* method:当前执行的方法对象
* args:执行方法所需的参数
* 返回值:
* 当前执行方法的返回值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
String name = method.getName();
Float money = (Float) args[0];
Object rtValue = null;
//每个经纪公司对不同演出收费不一样,此处开始判断
if("basicAct".equals(name)){
//基本演出,没有 2000 不演
if(money > 2000){
//看上去剧组是给了 8000,实际到演员手里只有 4000
//这就是我们没有修改原来 basicAct 方法源码,对方法进行了增强
rtValue = method.invoke(actor, money/2);
}
}
if("dangerAct".equals(name)){
//危险演出,没有 5000 不演
if(money > 5000){
//看上去剧组是给了 50000,实际到演员手里只有 25000
//这就是我们没有修改原来 dangerAct 方法源码,对方法进行了增强
rtValue = method.invoke(actor, money/2);
}
}
return rtValue;
}
});
//没有经纪公司的时候,直接找演员。
//
actor.basicAct(1000f);
//
actor.dangerAct(5000f);
//剧组无法直接联系演员,而是由经纪公司找的演员
proxyActor.basicAct(8000f);
proxyActor.dangerAct(50000f);
}
}
还是那个演员的例子,只不过不让他实现接口。
/**
* 一个演员
*/
public class Actor{//没有实现任何接口
public void basicAct(float money){
System.out.println("拿到钱,开始基本的表演:"+money);
}
public void dangerAct(float money){
System.out.println("拿到钱,开始危险的表演:"+money);
}
}
public class Client {
/**
* 基于子类的动态代理
* 要求:
*
被代理对象不能是最终类
* 用到的类:
*
Enhancer
* 用到的方法:
*
create(Class, Callback)
* 方法的参数:
*
Class:被代理对象的字节码
*
Callback:如何代理
* @param args
*/
public static void main(String[] args) {
final Actor actor = new Actor();
Actor cglibActor = (Actor) Enhancer.create(actor.getClass(), new MethodInterceptor() {
/**
* 执行被代理对象的任何方法,都会经过该方法。在此方法内部就可以对被代理对象的任何
方法进行增强。
*
* 参数:
* 前三个和基于接口的动态代理是一样的。
* MethodProxy:当前执行方法的代理对象。
* 返回值:
* 当前执行方法的返回值
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
String name = method.getName();
Float money = (Float) args[0];
Object rtValue = null;
if("basicAct".equals(name)){
//基本演出
if(money > 2000){
rtValue = method.invoke(actor, money/2);
}
}
if("dangerAct".equals(name)){
//危险演出
if(money > 5000){
rtValue = method.invoke(actor, money/2);
}
}
return rtValue;
}
});
cglibActor.basicAct(10000);
cglibActor.dangerAct(100000);
}
}
spring 的 aop,就是通过配置的方式实现代理的功能。
在 spring 中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式。
创建 spring 的配置文件并导入约束
此处要导入 aop 的约束
<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"
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">
beans>
配置 spring 的 ioc
!-- 配置 service -->
<bean id="accountService" class="org.woster.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao">property>
bean>
<bean id="accountDao" class="org.woster.dao.impl.AccountDaoImpl">
<property name="dbAssit" ref="dbAssit">property>
bean>
<bean id="dbAssit" class="org.woster.dbassit.DBAssit">
<property name="dataSource" ref="dataSource">property>
<property name="useCurrentConnection" value="true">property>
bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver">property>
<property name="jdbcUrl" value="jdbc:mysql:///test">property>
<property name="user" value="root">property>
<property name="password" value="root">property>
bean>
抽取公共代码制作成通知
public class TransactionManager {
//定义一个 DBAssit
private DBAssit dbAssit ;
public void setDbAssit(DBAssit dbAssit) {
this.dbAssit = dbAssit;
}
//开启事务
public void beginTransaction() {
try {
dbAssit.getCurrentConnection().setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
//提交事务
public void commit() {
try {
dbAssit.getCurrentConnection().commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
//回滚事务
public void rollback() {
try {
dbAssit.getCurrentConnection().rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
//释放资源
public void release() {
try {
dbAssit.releaseConnection();
} catch (Exception e) {
e.printStackTrace();
}
}
}
第一步:把通知类用 bean 标签配置起来
<bean id="txManager" class="org.woster.utils.TransactionManager">
<property name="dbAssit" ref="dbAssit">property>
bean>
第二步:使用 aop:config 声明 aop 配置
aop:config:
作用:
<aop:config>
aop:config>
第三步:使用 aop:aspect 配置切面
aop:aspect:
作用:
属性:
<aop:aspect id="txAdvice" ref="txManager">
aop:aspect>
第四步:使用 aop:pointcut 配置切入点表达式
aop:pointcut:
作用:
属性:
<aop:pointcut expression="execution(public void org.woster.service.impl.AccountServiceImpl.transfer(java.lang.String, java.lang.String,java.lang.Float)
)" id="pt1"/>
第五步:使用 aop:xxx 配置对应的通知类型
aop:before
作用:
属性:
执行时间点:
aop:after-returning
作用:
属性:
执行时间点:
aop:after-throwing
作用:
属性:
执行时间点:
aop:after
作用:
属性:
执行时间点:
execution:匹配方法的执行
execution(表达式)
表达式语法:execution([修饰符] 返回值类型 包名.类名.方法名(参数))
写法说明
全匹配方式:
public void org.woster.service.impl.AccountServiceImpl.saveAccount(org.woster.domain.Account)
访问修饰符可以省略
void org.woster.service.impl.AccountServiceImpl.saveAccount(org.woster.domain.Account)
返回值可以使用*号,表示任意返回值
* org.woster.service.impl.AccountServiceImpl.saveAccount(org.woster.domain.Account)
包名可以使用*号,表示任意包,但是有几级包,需要写几个*
* *.*.*.*.AccountServiceImpl.saveAccount(org.woster.domain.Account)
使用..来表示当前包,及其子包
* com..AccountServiceImpl.saveAccount(org.woster.domain.Account)
类名可以使用*号,表示任意类
* com..*.saveAccount(org.woster.domain.Account)
方法名可以使用*号,表示任意方法
* com..*.*( org.woster.domain.Account)
参数列表可以使用*,表示参数可以是任意数据类型,但是必须有参数
* com..*.*(*)
参数列表可以使用..表示有无参数均可,有参数可以是任意类型
* com..*.*(..)
全通配方式
* *..*.*(..)
通常情况下,我们都是对业务层的方法进行增强,所以切入点表达式都是切到业务层实现类。execution(* org.woster.service.impl..(…))
配置方式
<aop:config>
<aop:pointcut expression="execution(* org.woster.service.impl.*.*(..))" id="pt1"/>
<aop:aspect id="txAdvice" ref="txManager">
<aop:around method="transactionAround" pointcut-ref="pt1"/>
aop:aspect>
aop:config>
aop:around:
作用:
属性:
说明:
注意:
/**
* 环绕通知
* @param pjp
* spring 框架为我们提供了一个接口:ProceedingJoinPoint,它可以作为环绕通知的方法参数。
* 在环绕通知执行时,spring 框架会为我们提供该接口的实现类对象,我们直接使用就行。
* @return
*/
public Object transactionAround(ProceedingJoinPoint pjp) {
//定义返回值
Object rtValue = null;
try {
//获取方法执行所需的参数
Object[] args = pjp.getArgs();
//前置通知:开启事务
beginTransaction();
//执行方法
rtValue = pjp.proceed(args);
//后置通知:提交事务
commit();
}catch(Throwable e) {
//异常通知:回滚事务
rollback();
e.printStackTrace();
}finally {
//最终通知:释放资源
release();
}
return rtValue;
}
在配置文件中导入 context 的名称空间
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
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
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">
<bean id="dbAssit" class="org.woster.dbassit.DBAssit">
<property name="dataSource" ref="dataSource">property>
<property name="useCurrentConnection" value="true">property>
bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver">property>
<property name="jdbcUrl" value="jdbc:mysql:///spring_day02">property>
<property name="user" value="root">property>
<property name="password" value="1234">property>
bean>
beans>
把资源使用注解配置
@Service("accountService")
public class AccountServiceImpl implements IAccountService {
@Autowired
private IAccountDao accountDao;
}
@Repository("accountDao")
public class AccountDaoImpl implements IAccountDao {
@Autowired
private DBAssit dbAssit ;
}
在配置文件中指定 spring 要扫描的包
<context:component-scan base-package="org.woster">context:component-scan>
第一步:把通知类也使用注解配置
@Component("txManager")
public class TransactionManager {
//定义一个 DBAssit
@Autowired
private DBAssit dbAssit ;
}
第二步:在通知类上使用@Aspect 注解声明为切面
作用:
@Component("txManager")
@Aspect//表明当前类是一个切面类
public class TransactionManager {
//定义一个 DBAssit
@Autowired
private DBAssit dbAssit ;
}
第三步:在增强的方法上使用注解配置通知
@Before
作用:
属性:
//开启事务
@Before("execution(* org.woster.service.impl.*.*(..))")
public void beginTransaction() {
try {
dbAssit.getCurrentConnection().setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
@AfterReturning
作用:
属性:
//提交事务
@AfterReturning("execution(* org.woster.service.impl.*.*(..))")
public void commit() {
try {
dbAssit.getCurrentConnection().commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
@AfterThrowing
作用:
属性:
//回滚事务
@AfterThrowing("execution(* org.woster.service.impl.*.*(..))")
public void rollback() {
try {
dbAssit.getCurrentConnection().rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
@After
作用:
属性:
//释放资源
@After("execution(* org.woster.service.impl.*.*(..))")
public void release() {
try {
dbAssit.releaseConnection();
} catch (Exception e) {
e.printStackTrace();
}
}
第四步:在 spring 配置文件中开启 spring 对注解 AOP 的支持
<aop:aspectj-autoproxy/>
@Around
作用:
属性:
@Around("execution(* org.woster.service.impl.*.*(..))")
public Object transactionAround(ProceedingJoinPoint pjp) {
//定义返回值
Object rtValue = null;
try {
//获取方法执行所需的参数
Object[] args = pjp.getArgs();
//前置通知:开启事务
beginTransaction();
//执行方法
rtValue = pjp.proceed(args);
//后置通知:提交事务
commit();
}catch(Throwable e) {
//异常通知:回滚事务
rollback();
e.printStackTrace();
}finally {
//最终通知:释放资源
release();
}
return rtValue;
}
@Pointcut
作用:
属性:
@Pointcut("execution(* org.woster.service.impl.*.*(..))")
private void pt1() {}
引用方式:
/**
* 环绕通知
* @param pjp
* @return
*/
@Around("pt1()")//注意:千万别忘了写括号
public Object transactionAround(ProceedingJoinPoint pjp) {
//定义返回值
Object rtValue = null;
try {
//获取方法执行所需的参数
Object[] args = pjp.getArgs();
//前置通知:开启事务
beginTransaction();
//执行方法
rtValue = pjp.proceed(args);
//后置通知:提交事务
commit();
}catch(Throwable e) {
//异常通知:回滚事务
rollback();
e.printStackTrace();
}finally {
//最终通知:释放资源
release();
}
return rtValue;
}
@Configuration
@ComponentScan(basePackages="org.woster")
@EnableAspectJAutoProxy
public class SpringConfiguration {
}