1 现有业务层开发存在问题
1.定义业务接口的代码如下:
package com.txw.service;
/**
* @author Adair
* E-mail: [email protected]
*/
@SuppressWarnings("all") // 注解警告信息
public interface UserService {
/**
* 保存
* @param name
*/
public void save(String name);
/**
* 根据id删除
* @param id
*/
public void delete(String id);
/**
* 修改
*/
public void update();
/**
* 根据名字查询所有
* @param name
* @return
*/
public String findAll(String name);
/**
* 根据id查询一个
* @param id
* @return
*/
public String findOne(String id);
}
如图所示:
2.实现业务接口的代码如下:
package com.txw.service.impl;
import com.txw.service.UserService;
/**
* @author Adair
* E-mail: [email protected]
*/
@SuppressWarnings("all") // 注解警告信息
public class UserServiceImpl implements UserService {
/**
* 保存
* @param name
*/
@Override
public void save(String name) {
try {
System.out.println("开启事务");
System.out.println("处理业务逻辑,调用DAO~~~");
System.out.println("提交事务");
}catch (Exception e){
System.out.println("回滚事务");
e.printStackTrace();
}
}
/**
* 根据id删除
* @param id
*/
@Override
public void delete(String id) {
try {
System.out.println("开启事务");
System.out.println("处理业务逻辑,调用DAO~~~");
System.out.println("提交事务");
}catch (Exception e){
System.out.println("回滚事务");
e.printStackTrace();
}
}
/**
* 修改
*/
@Override
public void update() {
try {
System.out.println("开启事务");
System.out.println("处理业务逻辑,调用DAO~~~");
System.out.println("提交事务");
}catch (Exception e){
System.out.println("回滚事务");
e.printStackTrace();
}
}
/**
* 根据名字查询所有
* @param name
* @return
*/
@Override
public String findAll(String name) {
try {
System.out.println("开启事务");
System.out.println("处理业务逻辑,调用DAO~~~");
System.out.println("提交事务");
}catch (Exception e){
System.out.println("回滚事务");
e.printStackTrace();
}
return name;
}
/**
* 根据id查询一个
* @param id
* @return
*/
@Override
public String findOne(String id) {
try {
System.out.println("开启事务");
System.out.println("处理业务逻辑,调用DAO~~~");
System.out.println("提交事务");
}catch (Exception e){
System.out.println("回滚事务");
e.printStackTrace();
}
return id;
}
}
如图所示:
问题:从上图中可以看出,现有业务层中控制事务代码出现了大量的冗余
,如何解决现有业务层出现的冗余问题?
3.编写spring.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="userService" class="com.txw.service.impl.UserServiceImpl"></bean>
</beans>
如图所示:
4.编写UserServiceTest的代码如下:
package com.txw.test;
import com.txw.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 测试
* @author Adair
* E-mail: [email protected]
*/
@SuppressWarnings("all") // 注解警告信息
public class UserServiceTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) context.getBean("userService");
userService.save("Adair");
}
}
如图所示:
2 代理引言
2.1 什么是代理
代理
: 指的是java中的一种设计模式
。
2.2 为什么需要代理
很多时候除了当前类能够提供的功能外,我们还需要补充一些额外功能。
2.3 代理的作用
代理对象可以在客户和目标对象之间
起到中介作用,从而为目标对象增添额外的功能
。
2.4 代理图例
3 静态代理的开发
目标类|对象(target)
:被代理类称之为目标类|或者被代理的对象的称之为目标对象。
开发代理的原则: 代理类和目标类功能一致且实现相同的接口,同时代理类中依赖于目标类对象
。
3.1 开发静态代理类
代码如下:
package com.txw.util;
import com.txw.service.UserService;
/**
* 静态代理类
* 开发原则:代理类和目标类实现相同接口,依赖于真正的目标类
* @author Adair
* E-mail: [email protected]
*/
@SuppressWarnings("all") // 注解警告信息
public class UserServiceStaticProxy implements UserService {
// 真正的目标类
// target 原始业务逻辑对象
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
/**
* 保存
* @param name
*/
@Override
public void save(String name) {
try {
System.out.println("开启事务");
userService.save(name); // 调用真正业务逻辑方法
System.out.println("提交事务");
}catch (Exception e){
System.out.println("回滚事务");
e.printStackTrace();
}
}
/**
* 根据id删除
* @param id
*/
@Override
public void delete(String id) {
try {
System.out.println("开启事务");
userService.delete(id); // 调用真正业务逻辑方法
System.out.println("提交事务");
}catch (Exception e){
System.out.println("回滚事务");
e.printStackTrace();
}
}
/**
* 修改
*/
@Override
public void update() {
try {
System.out.println("开启事务");
userService.update(); // 调用真正业务逻辑方法
System.out.println("提交事务");
}catch (Exception e){
System.out.println("回滚事务");
e.printStackTrace();
}
}
/**
* 根据名字查询所有
* @param name
* @return
*/
@Override
public String findAll(String name) {
try {
System.out.println("开启事务");
String result = userService.findAll(name); // 调用真正业务逻辑方法
System.out.println("提交事务");
return result;
}catch (Exception e){
System.out.println("回滚事务");
e.printStackTrace();
}
return null;
}
/**
* 根据id查询一个
* @param id
* @return
*/
@Override
public String findOne(String id) {
try {
System.out.println("开启事务");
// 调用目标类方法
String one = userService.findOne(id); // 调用真正业务逻辑方法
System.out.println("提交事务");
return one;
}catch (Exception e){
System.out.println("回滚事务");
e.printStackTrace();
}
return null;
}
}
如图所示:
3.2 更改目标实现类
代码如下:
package com.txw.service.impl;
import com.txw.service.UserService;
/**
* @author Adair
* E-mail: [email protected]
*/
@SuppressWarnings("all") // 注解警告信息
public class UserServiceImpl implements UserService {
/**
* 保存
* @param name
*/
@Override
public void save(String name) {
System.out.println("处理业务逻辑,调用DAO~~~");
}
/**
* 根据id删除
* @param id
*/
@Override
public void delete(String id) {
System.out.println("处理业务逻辑,调用DAO~~~");
}
/**
* 修改
*/
@Override
public void update() {
System.out.println("处理业务逻辑,调用DAO~~~");
}
/**
* 根据名字查询所有
* @param name
* @return
*/
@Override
public String findAll(String name) {
System.out.println("处理业务逻辑,调用DAO~~~");
return name;
}
/**
* 根据id查询一个
* @param id
* @return
*/
@Override
public String findOne(String id) {
System.out.println("处理业务逻辑,调用DAO~~~");
return id;
}
}
如图所示:
3.3 配置静态代理类
代码如下:
<?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="userService" class="com.txw.service.impl.UserServiceImpl"></bean>
<!--配置代理类-->
<bean id="serviceStaticProxy" class="com.txw.util.UserServiceStaticProxy">
<!--注入目标对象-->
<property name="userService" ref="userService"/>
</bean>
</beans>
如图所示:
3.4 调用代理方法
代码如下:
package com.txw.test;
import com.txw.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 测试
* @author Adair
* E-mail: [email protected]
*/
@SuppressWarnings("all") // 注解警告信息
public class UserServiceTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService serviceStaticProxy = (UserService) context.getBean("serviceStaticProxy");
serviceStaticProxy.save("Adair");
}
}
如图所示:
新的问题:
往往在开发我们书写的不仅仅是一个业务层,两个业务层,而我们的业务层会有很多,如果为每一个业务层开发一个静态代理类,不仅没有减轻工作量,甚至让我们的工作量多了一倍不止怎么解决以上这个问题呢?
解决方案:为业务层在运行过程中动态创建代理类,通过动态代理类去解决我们现有业务层中业务代码冗余的问题
。
4 动态代理的原理
通过jdk提供的Proxy这个类,动态为现有的业务生成代理类
。
参数一:当前线程类加载器。
参数二:生成代理类的接口类型。
参数三:通过代理类对象调用方法时会优先进入参数三中的invoke方Proxy.newProxyInstance(loader, interfaces, h);//返回值就是动态代理对象。
演示的代码如下:
package com.txw.test;
import com.txw.service.UserService;
import com.txw.service.impl.UserServiceImpl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 动态测试类
* @author Adair
* E-mail: [email protected]
*/
@SuppressWarnings("all") // 注解警告信息
public class TestDynamicProxy {
public static void main(String[] args) {
final UserService userService = new UserServiceImpl();
// 参数1:当前线程类加载器
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
// 参数2:
Class[] classes = new Class[]{
UserService.class};
// 参数3:
UserService userServiceProxy = (UserService) Proxy.newProxyInstance(contextClassLoader, classes, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try{
System.out.println("开启事务"); // 附加操作
Object invoke = method.invoke(userService, args);
System.out.println("提交事务"); // 附加操作
return invoke;
}catch (Exception e){
System.out.println("回滚事务"); // 附加操作
}
return null;
}
});
userServiceProxy.save("小黑");
}
}
5 AOP (Aspect Oriented Programming)编程
通知(Advice)
:除了目标方法以外的操作都称之为通知
。
切入点(PointCut): 要为哪些类中的哪些方法加入通知
。
切面(Aspect)
:通知 + 切入点
。
5.1 通知分类
如图所示:
5.2 编程步骤
1.引入依赖,如图所示:
2.开发通知类
1.编写的EmpService的代码如下:
package com.txw.service;
/**
* @author Adair
* E-mail: [email protected]
*/
@SuppressWarnings("all") // 注解警告信息
public interface EmpService {
public void ma();
public void mb(String name);
public String mc(String id);
}
如图所示:
2.编写的EmpServiceImpl的代码如下:
package com.txw.service.impl;
import com.txw.service.EmpService;
/**
* 核心业务对象 被代理的对象 目标对象
* @author Adair
* E-mail: [email protected]
*/
@SuppressWarnings("all") // 注解警告信息
public class EmpServiceImpl implements EmpService {
@Override
public void ma() {
System.out.println("处理 ma 核心业务逻辑");
}
@Override
public void mb(String name) {
System.out.println("处理 mb 核心业务逻辑");
}
@Override
public String mc(String id) {
System.out.println("处理 mc 核心业务逻辑");
return id;
}
}
如图所示:
3.配置切面
在spring1.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: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">
<!--管理 核心业务对象 目标对象-->
<bean id="empService" class="com.txw.service.impl.EmpServiceImpl"></bean>
<!--管理 通知-->
<bean id="boforeAdivce" class="com.txw.before.MyBoforeAdivce"></bean>
<!--配置切面-->
<aop:config>
<!--配置切入点
id: 切入点表示 唯一
execution:切入点表达式
-->
<aop:pointcut id="pc1" expression="execution(* com.txw.service.impl.*.*(..))"/>
<!--配置切入点 额外功能
advice-ref:指定添加的通知
pointcut-ref:指定要使用切入点
-->
<aop:advisor advice-ref="boforeAdivce" pointcut-ref="pc1"/>
</aop:config>
</beans>
如图所示:
4.启动工厂测试
.编写SpringTest的代码如下:
package com.txw.test;
import com.txw.service.EmpService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 测试
* @author Adair
* E-mail: [email protected]
*/
public class SpringTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring1.xml");
// 当前获取的核心业务对象 是通过动态代理生成的代理对象
EmpService empService = (EmpService) context.getBean("empService");
empService.ma();
}
}
如图所示:
5.3 前置通知的使用
代码如下:
package com.txw.before;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
/**
* @author Adair
* E-mail: [email protected]
*/
@SuppressWarnings("all") // 注解警告信息
public class MyBoforeAdivce implements MethodBeforeAdvice {
/**
*
* @param method 目标类中当前调用的方法对象
* @param objects 当前调用方法的参数
* @param o 目标对象
* @throws Throwable
*/
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("日志通知类记录方法的名字为:" +method);
}
}
如图所示:
5.4 环绕通知的使用
如图所示:
代码如下:
package com.txw.before;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
* 环绕通知
* @author Adair
* E-mail: [email protected]
*/
@SuppressWarnings("all") // 注解警告信息
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
// 书写额外功能
long begin = System.currentTimeMillis();
System.out.println("方法开始执行!!!");
// 调用核心业务对象 核心服务方法
Object result = methodInvocation.proceed();// 放行 执行核心服务方法 等价于动态代理 method.invoke(new xxxServiceImpl,args)
System.out.println("方法即将结束!!!");
long end = System.currentTimeMillis();
System.out.println("方法运行时长为:" + (end =begin));
/* System.out.println("方法名:" +methodInvocation.getMethod().getName());
System.out.println("参数");
for (Object argument : methodInvocation.getArguments()) {
System.out.println((String) argument);
}*/
return result;
}
}
如图所示:
在spring1.xml添加的代码如下:
<!--环绕通知-->
<bean id="myMethodInterceptor" class="com.txw.before.MyMethodInterceptor"></bean>
<!--配置切面-->
<aop:config>
<aop:pointcut id="PC1" expression="execution(* com.txw.service.impl.*.*(..))"/>
<aop:advisor advice-ref="myMethodInterceptor" pointcut-ref="pc1"/>
</aop:config>
如图所示:
5.5 返回后通知
代码如下:
package com.txw.before;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
/**
* 后置通知
* @author Adair
* E-mail: [email protected]
*/
@SuppressWarnings("all") // 注解警告信息
public class AtferReturnimgAdvice implements AfterReturningAdvice {
/**
* 主体逻辑:此中的过程在核心业务执行之后执行
* @param o 目标方法返回值 如果方法是void 则o为null
* @param method 目标方法
* @param objects 方法参数列表
* @param o1 目标
* @throws Throwable
*/
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
}
}
如图所示:
在spring1.xml添加的代码如下:
<!--后置通知-->
<bean id="atferReturnimgAdvice" class="com.txw.before.AtferReturnimgAdvice"></bean>
<!--配置切面-->
<aop:config>
<aop:pointcut id="PC1" expression="execution(* com.txw.service.impl.*.*(..))"/>
<aop:advisor advice-ref="atferReturnimgAdvice" pointcut-ref="pc1"/>
</aop:config>
如图所示:
5.6 异常通知在spring1.xml添加的代码,如图所示:## 6 切入点表达表
6.1 execution方法级别的切入点表达式
如图所示:
注意:方法级别的切入点表达式尽可能精准,否则程序运行可能出现异常`
6.2 within类级别的切入点表达式
1.语法如下:
within(包.类)
2.演示的代码,如图所示:
注意:within的效率高于execution表达式,推荐使用within表达式