在实际开发中,客户的需求可能会随着软件的开发而发生变化。如果在项目前期的时候,业务需求的变化对项目的影响可能不是很大。但是如果到了项目后期阶段,这时候软件的功能已经基本完成。那么如果客户这时候提出修改需求,这样可能会导致项目的影响是很大的,严重的焕可能会导致项目推动重来。
使用代理技术可以解决由于需求变化对业务代码的影响。Java代理技术的优势是实现无嵌入式的代码扩展,使得用户可以在不修改原有代码的基础上额外增加新的功能。
静态代理是通过创建一个代理类,对已有类的方法进行功能的扩展。
1)代码直观,易于阅读;
2)代理类在程序编译之前就已经创建出来,执行效率高;
1)当存在多个目标需要代理的时候,必须要为每个目标类创建代理类,比较繁琐;
2)每个代理类中可能会存在相同的代码,程序的维护性较差;
3)由于代理类在程序编译前就定义好了,灵活性较差
例如:银行和催收公司:
package com.chinasofti.demo01静态代理;
interface Agent {
// 讨债
void taozhai();
}
class IcbcBank implements Agent {
@Override
public void taozhai() {
System.out.println("把钱打入工商银行账号...");
System.out.println("还清...");
}
}
class ChinaBank implements Agent {
@Override
public void taozhai() {
System.out.println("把钱打入中国银行账号...");
System.out.println("还清...");
}
}
// 催收公司(代理)
class DebtAgent implements Agent {
int num = 0;
public DebtAgent(int num) {
this.num = num;
}
@Override
public void taozhai() {
System.out.println("打电话问候...");
System.out.println("上门问候...");
System.out.println("淋红油...");
if (num == 1) {
IcbcBank bank = new IcbcBank();
bank.taozhai();
} else if (num == 2) {
ChinaBank cb = new ChinaBank();
cb.taozhai();
}
}
}
public class Demo01 {
public static void main(String[] args) {
DebtAgent agent = new DebtAgent(2);
agent.taozhai();
}
}
与静态代理不同,动态代理的特点:
jdk提供了java.lang.reflect.Proxy类,该类提供了创建动态代理对象的方法。
package com.chinasofti.demo02动态代理;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface Agent {
// 讨债
void taozhai();
}
class IcbcBank implements Agent {
@Override
public void taozhai() {
System.out.println("把钱打入工商银行账号...");
System.out.println("还清...");
}
}
class ChinaBank implements Agent {
@Override
public void taozhai() {
System.out.println("把钱打入中国银行账号...");
System.out.println("还清...");
}
}
public class Demo02 {
public static void main(String[] args) {
// 被代理的目标对象
ChinaBank bank = new ChinaBank();
// 创建代理对象
Agent bankProxy = (Agent) Proxy.newProxyInstance(
bank.getClass().getClassLoader(), // 与目标对象的ClassLoader相同
bank.getClass().getInterfaces(), // 与目标对象实现的接口相同,用来指定代理对象实现的接口
new InvocationHandler() {
/*
* 执行被代理对象的任何方法,都会经过该方法。(non-Javadoc)
* proxy:动态代理对象的引用
* method:要执行的方法
* args:要执行方法的参数
*/
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
System.out.println("打电话问候...");
System.out.println("上门问候...");
System.out.println("淋红油...");
// 执行目标对象的方法
method.invoke(bank, args);
return null;
}
});
bankProxy.taozhai();
}
}
AOP(Aspect Oriented Programming):面向切面编程。他把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对我们的已有方法进行增强。
AOP的相关术语:
名称 | 功能 |
---|---|
连接点(Joint Point) | 连接点是指那些被拦截的方法 |
切入点(Pointcut) | 切入点是指对Jointpoint进行拦截的定义 |
通知(Advice) | 通知是指拦截到Joinpoint之后所要做的事情 |
目标对象(Target) | 被代理的对象 |
代理(Proxy) | 增强后的对象 |
切面(Aspect) | 切入点和通知的结合 |
第一步:导入AOP相关的jar包:
aopalliance-1.0.jar
aspectjweaver-1.9.4.jar
spring-aop-5.1.9.RELEASE.jar
spring-aspects-5.1.9.RELEASE.jar
第二步:开启AOP的支持:
<aop:aspectj-autoproxy/>
第三步:定义切面类:
@Component
@Aspect
public class Logger {
}
第四步:定义通知:
@Before("execution(* com.xjy.service.impl.*.*(..))")
public void before() {
System.out.println("前置通知...");
}
第五步:测试:
@Test
public void testFindAll() {
ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:beans.xml");
ICustomerService customerService = (ICustomerService) ac.getBean("customerServiceImpl");
customerService.findAllCustomer();
}
@AfterReturning和@After的区别?
1)它们都是在切入方法执行完后执行,但是@AfterReturning在@After之后执行;
2)如果方法出现异常,那么@AfterReturning将不会执行;而@After无论方法是否正常结束,它都会被执行;
3)@AfterReturning可以访问目标方法的返回值,而@After就无法做到
@AfterReturning(value="execution(* com.xjy.service.impl.*.*(..))", returning="rt")
public void afterReturn(Object rt) throws Throwable {
System.out.println("切入方法的返回值:" + rt);
System.out.println("方法返回通知...");
}
注意:returning属性值要与增强方法的参数名相同。
首先创建一个配置类,使用@EnableAspectJAutoProxy注解启用AOP功能。
@Configuration
@ComponentScan(basePackages={"com.chinasofti"})
// 启用aop功能
@EnableAspectJAutoProxy
class SpringConfig {
}
然后在main方法中使用AnnotationConfigApplicationContext创建Spring容器。
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
第一步:导入jar包
mybatis-spring-1.3.2.jar
第二步:在映射接口上使用@Repository注解
@Repository
public interface UserMapper {
void addUser(User user);
}
第三步:把Mapper接口注入到业务层
public class UserServiceImpl implements IUserService {
@Autowired
private UserMapper userMapper;
}
第四步:编写测试类
package springqs.test;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import springqs.beans.User;
import springqs.service.IUserService;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={UserServiceTest.class})
@Configuration
@ComponentScan(basePackages={"com.lmc"})
@MapperScan("com.lmc.mapper")
public class UserServiceTest {
@Autowired
private IUserService userService;
@Bean(name="dataSource")
public DataSource getDataSource() {
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/test");
ds.setUsername("root");
ds.setPassword("root");
return ds;
}
@Bean
public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource")DataSource ds) throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(ds);
return sessionFactory.getObject();
}
@Test
public void testAddUser() {
User user = new User();
user.setName("ddee");
user.setAge(12);
userService.addUser(user);
}
}