P89 end
P90 JdbcTemplate之回调函数
其它类中的内容与上集相同
测试类中的内容:
@Test
public void testCallback(){
//如果你想写JDBC代码,可以使用callback回调函数。
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = applicationContext.getBean("jdbcTemplate", JdbcTemplate.class);
//准备SQL语句
String sql = "select id, real_name, age from t_user where id = ?";
//注册回调函数,当execute方法执行的时候,回调函数中的doInPreparedStatement()会被调用。
User user = jdbcTemplate.execute(sql, new PreparedStatementCallback<User>() {
@Override
public User doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {
User user = null;
ps.setInt(1,2);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
int id = rs.getInt("id");
String realName = rs.getString("real_name");
int age = rs.getInt("age");
user = new User(id,realName,age);
}
return user;
}
});
System.out.println(user);
}
P90 end
P91 JdbcTemplate之整合德鲁伊连接池
P91 start :
使用德鲁伊连接池需要引入德鲁伊连接池的依赖。(毕竟别人写的阿里巴巴开发的)
<!--引入德鲁伊连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.8</version>
</dependency>
spring配置文件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">
<!--配置自己写的数据源 -->
<!--当然,也可以集成其他人或者其他组织开发的数据源,例如:c3p0 dbcp druid -->
<!-- <bean id="ds" class="com.powernode.spring6.bean.MyDataSource">-->
<!-- <property name="driver" value="com.mysql.cj.jdbc.Driver"/>-->
<!-- <property name="url" value="jdbc:mysql://localhost:3306/spring6"/>-->
<!-- <property name="username" value="root"/>-->
<!-- <property name="password" value="123456"/>-->
<!-- </bean>-->
<!--引入德鲁伊连接池 -->
<bean id="ds" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring6"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--配置JdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="ds"/>
</bean>
</beans>
测试类与上集相同
P91 end
P92 GoF代理模式之对代理模式的深入理解
P92 start :
- 代理模式的作用:
- 1)当一个对象需要受到保护时,可以考虑使用代理模式去完成某个行为
- 2)需要给某个对象的功能进行增强时,可以考虑找一个代理进行增强
- 3)A 对象和 B 对象无法直接进行交互时,也可以使用代理模式来解决
什么时候使用代理:
1,在程序中,对象A和对象B无法直接交互时
2,在程序中,功能需要增强时。
3,在程序中,目标需要被保护时
4,在程序中,目标不但受到保护,并且代码也得到了复用
代理模式是GoF23种设计模式之一。属于结构型设计模式。
代理模式中的角色:
- 代理类(代理主题)
- 目标类(真实主题)
- 代理类和目标类的公共接口(抽象主题):客户端在使用代理类时就像在使用目标类,不被客户端所察觉,所以代理类和目标类要有共同的行为,也就是实现共同的接口。
代理模式在代码实现上,包括两种形式
- 静态代理
- 动态代理
OrderService接口中的内容:
package com.powernode.proxy.service;
/**
* 订单业务接口
*/
public interface OrderService {
/**
* 生成订单
*/
void generate() throws InterruptedException;
/**
* 修改订单信息
*/
void modify();
/**
* 查看订单详情
*/
void detail();
}
OrderServiceImpl类中的内容:
package com.powernode.proxy.service;
public class OrderServiceImpl implements OrderService{
/**
* 项目经理提出一个新的需求:要统计所有业务接口中每一个业务方法的耗时。
* 解决方案一:硬编码,在每一个业务接口中的每一个业务方法中直接添加统计耗时的程序。
* 这种方案的缺点:
* 缺点一:违背OCP开闭原则。
* 缺点二:代码没有得到复用。(相同的代码写了很多遍)
* 解决方案二:编写业务类的子类,让子类继承业务类,对每个业务方法进行重写。
* 缺点一:虽然解决了OCP开闭原则。但是这种方式会导致耦合度很高,因为采用了继承关系。
* 继承关系是一种耦合度非常高的关系,不建议使用。
* 缺点二:代码没有得到复用。(相同的代码写了很多遍)
* 解决方案三:代理模式。
*/
// @Override
// public void generate(){
// long begin = System.currentTimeMillis();
// //模拟生成订单的耗时
// try{
// Thread.sleep(1234);
// }catch(InterruptedException e){
// e.printStackTrace();
// }
//
// System.out.println("订单已生成.");
// long end = System.currentTimeMillis();
// System.out.println("耗时" + (end - begin) + "耗时");
// }
//
// @Override
// public void modify() {
// long begin = System.currentTimeMillis();
// //模拟修改订单的耗时
// try{
// Thread.sleep(456);
// }catch(InterruptedException e){
// e.printStackTrace();
// }
// System.out.println("订单已修改.");
// long end = System.currentTimeMillis();
// System.out.println("耗时" + (end - begin) + "耗时");
// }
//
// @Override
// public void detail() {
// long begin = System.currentTimeMillis();
// //模拟查询订单的耗时
// try{
// Thread.sleep(111);
// }catch(InterruptedException e){
// e.printStackTrace();
// }
// System.out.println("请查看订单详情.");
// long end = System.currentTimeMillis();
// System.out.println("耗时" + (end - begin) + "耗时");
// }
@Override
public void generate(){
//模拟生成订单的耗时
try{
Thread.sleep(1234);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("订单已生成.");
}
@Override
public void modify() {
//模拟修改订单的耗时
try{
Thread.sleep(456);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("订单已修改.");
}
@Override
public void detail() {
//模拟查询订单的耗时
try{
Thread.sleep(111);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("请查看订单详情.");
}
}
OrderServiceImplSub类中的 内容:
package com.powernode.proxy.service;
public class OrderServiceImplSub extends OrderServiceImpl{
@Override
public void generate() {
long begin = System.currentTimeMillis();
super.generate();
long end = System.currentTimeMillis();
System.out.println("耗时" + (end - begin) + "毫秒");
}
@Override
public void modify() {
long begin = System.currentTimeMillis();
super.modify();
long end = System.currentTimeMillis();
System.out.println("耗时" + (end - begin) + "毫秒");
}
@Override
public void detail() {
long begin = System.currentTimeMillis();
super.detail();
long end = System.currentTimeMillis();
System.out.println("耗时" + (end - begin) + "毫秒");
}
}
测试类中的内容:
package com.powernode.proxy.client;
import com.powernode.proxy.service.OrderService;
import com.powernode.proxy.service.OrderServiceImpl;
import com.powernode.proxy.service.OrderServiceImplSub;
public class Test {
public static void main(String[] args) throws InterruptedException {
// OrderService orderService = new OrderServiceImpl();
// orderService.generate();
// orderService.detail();
// orderService.modify();
OrderService orderService = new OrderServiceImplSub();
orderService.generate();
orderService.detail();
orderService.modify();
}
}
P93 END
P94 GoF代理模式之静态代理代码实现。
类和类之间的关系:包括6种关系。其中两个关系是:
泛化关系
关联关系
泛化关系:继承 is a(A是B)
关联关系: has a (A有B)
相比来说:泛化关系的耦合度高于关联关系。优先选择使用关联关系。
(我们要面向接口编程)
字节码生成技术是个啥东东???
OrderService接口中的内容:
package com.powernode.proxy.service;
/**
* 订单业务接口
*/
public interface OrderService {//代理对象和目标对象的公共接口
/**
* 生成订单
*/
void generate();
/**
* 修改订单信息
*/
void modify();
/**
* 查看订单详情
*/
void detail();
}
OrderServiceImpl类中的内容:
package com.powernode.proxy.service;
public class OrderServiceImpl implements OrderService{//目标对象
/**
* 项目经理提出一个新的需求:要统计所有业务接口中每一个业务方法的耗时。
* 解决方案一:硬编码,在每一个业务接口中的每一个业务方法中直接添加统计耗时的程序。
* 这种方案的缺点:
* 缺点一:违背OCP开闭原则。
* 缺点二:代码没有得到复用。(相同的代码写了很多遍)
* 解决方案二:编写业务类的子类,让子类继承业务类,对每个业务方法进行重写。
* 缺点一:虽然解决了OCP开闭原则。但是这种方式会导致耦合度很高,因为采用了继承关系。
* 继承关系是一种耦合度非常高的关系,不建议使用。
* 缺点二:代码没有得到复用。(相同的代码写了很多遍)
* 解决方案三:代理模式。
* 优点1:解决了OCP问题。
* 有点2:采用代理模式的has a,可以降低耦合度。
*
* 目前我们使用的是静态代理,这个静态代理的缺点是什么?
* 类爆炸。假设系统中有1000个接口,那么每个接口都需要代理类,
* 这样类会急剧膨胀。不好维护。
* 怎么解决类爆炸问题?
* 可以使用动态代理来解决这个问题。
* 动态代理还是代理模式,只不过添加了字节码生成技术,可以在内存中为我们
* 动态的生成一个class字节码,这个字节码就是代理类。
* 在内存中动态的生成字节码代理类的技术。叫做:动态代理。
*/
// @Override
// public void generate(){
// long begin = System.currentTimeMillis();
// //模拟生成订单的耗时
// try{
// Thread.sleep(1234);
// }catch(InterruptedException e){
// e.printStackTrace();
// }
//
// System.out.println("订单已生成.");
// long end = System.currentTimeMillis();
// System.out.println("耗时" + (end - begin) + "耗时");
// }
//
// @Override
// public void modify() {
// long begin = System.currentTimeMillis();
// //模拟修改订单的耗时
// try{
// Thread.sleep(456);
// }catch(InterruptedException e){
// e.printStackTrace();
// }
// System.out.println("订单已修改.");
// long end = System.currentTimeMillis();
// System.out.println("耗时" + (end - begin) + "耗时");
// }
//
// @Override
// public void detail() {
// long begin = System.currentTimeMillis();
// //模拟查询订单的耗时
// try{
// Thread.sleep(111);
// }catch(InterruptedException e){
// e.printStackTrace();
// }
// System.out.println("请查看订单详情.");
// long end = System.currentTimeMillis();
// System.out.println("耗时" + (end - begin) + "耗时");
// }
@Override
public void generate(){//目标方法
//模拟生成订单的耗时
try{
Thread.sleep(1234);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("订单已生成.");
}
@Override
public void modify() {//目标方法
//模拟修改订单的耗时
try{
Thread.sleep(456);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("订单已修改.");
}
@Override
public void detail() {//目标方法
//模拟查询订单的耗时
try{
Thread.sleep(111);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("请查看订单详情.");
}
}
OrderServiceProxy类中的 内容:
package com.powernode.proxy.service;
//代理对象(代理对象和目标对象要具有相同的行为,就要实现同一个或者同一些接口)
//客户端在使用代理对象的时候就像在使用目标对象一样。
public class OrderServiceProxy implements OrderService{
//将目标对象作为代理对象的一个属性。这种关系叫做关联关系。比继承关系的耦合度低。
//代理对象中含有目标对象的引用。关联关系. has a
//注意:这里要写一个公共接口类型。因为公共接口耦合度低。
private OrderService target;//这就是目标对象.目标对象一定是实现了OrderService接口的。
//创建代理对象的时候,传一个目标对象给代理对象。
public OrderServiceProxy(OrderService target) {
this.target = target;
}
@Override
public void generate(){//代理方法
//增强
long begin = System.currentTimeMillis();
//调用目标对象的目标方法
target.generate();
long end = System.currentTimeMillis();
System.out.println("耗时"+(end - begin)+"毫秒");
}
@Override
public void modify() {//代理方法
//增强
long begin = System.currentTimeMillis();
//调用目标对象的目标方法
target.modify();
long end = System.currentTimeMillis();
System.out.println("耗时"+(end - begin)+"毫秒");
}
@Override
public void detail() {//代理方法
//增强
long begin = System.currentTimeMillis();
//调用目标对象的目标方法
target.detail();
long end = System.currentTimeMillis();
System.out.println("耗时"+(end - begin)+"毫秒");
}
}
测试类中的 内容:
package com.powernode.proxy.client;
import com.powernode.proxy.service.OrderService;
import com.powernode.proxy.service.OrderServiceImpl;
import com.powernode.proxy.service.OrderServiceImplSub;
import com.powernode.proxy.service.OrderServiceProxy;
public class Test {
public static void main(String[] args) throws InterruptedException {
// OrderService orderService = new OrderServiceImpl();
// orderService.generate();
// orderService.detail();
// orderService.modify();
// OrderService orderService = new OrderServiceImplSub();
// orderService.generate();
// orderService.detail();
// orderService.modify();
//创建目标对象
OrderService target = new OrderServiceImpl();
//创建代理对象
OrderService proxy = new OrderServiceProxy(target);
//调用代理对象的代理方法
proxy.generate();
proxy.modify();
proxy.detail();
}
}
P94 end
P95 GoF代理模式之静态代理代码实现 start :
14.3 动态代理
在程序运行阶段,在内存中动态生成代理类,被称为动态代理,目标是为了减少代理类的数量。解决代码复用的问题。
在内存当中动态生成类的技术常见的包括:
- JDK动态代理技术:只能代理接口
- CGLIB动态代理技术:CGLIB(Code Generation Library)是一个开源项目。是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展java类与实现java接口。它既可以代理接口,又可以代理类,底层是通过继承的方式实现的。性能比JDK动态代理要好。(底层有一个小而快的字节码处理框架ASM.)
- Javassist动态代理技术:Javassist是一个开源的分析,编辑和创建java字节码的类库。是由东京工业大学的数学和计算机科学系的千叶滋所创建的。它已加入了开源代码JBoss应用服务器项目,通过使用javassist对字节码操作为JBoss实现动态"AOP"框架。
P95 end
P96 GoF代理模式之JDK动态代理的两个参数 start :
14.3.1JDK动态代理
orderService类中的的内容:
package com.powernode.proxy.service;
/**
* 订单业务接口
*/
public interface OrderService {//代理对象和目标对象的公共接口
/**
* 生成订单
*/
void generate();
/**
* 修改订单信息
*/
void modify();
/**
* 查看订单详情
*/
void detail();
}
OrderServiceImpl类中的内容:
package com.powernode.proxy.service;
public class OrderServiceImpl implements OrderService{//目标对象
/**
* 项目经理提出一个新的需求:要统计所有业务接口中每一个业务方法的耗时。
* 解决方案一:硬编码,在每一个业务接口中的每一个业务方法中直接添加统计耗时的程序。
* 这种方案的缺点:
* 缺点一:违背OCP开闭原则。
* 缺点二:代码没有得到复用。(相同的代码写了很多遍)
* 解决方案二:编写业务类的子类,让子类继承业务类,对每个业务方法进行重写。
* 缺点一:虽然解决了OCP开闭原则。但是这种方式会导致耦合度很高,因为采用了继承关系。
* 继承关系是一种耦合度非常高的关系,不建议使用。
* 缺点二:代码没有得到复用。(相同的代码写了很多遍)
* 解决方案三:代理模式。
* 优点1:解决了OCP问题。
* 有点2:采用代理模式的has a,可以降低耦合度。
*
* 目前我们使用的是静态代理,这个静态代理的缺点是什么?
* 类爆炸。假设系统中有1000个接口,那么每个接口都需要代理类,
* 这样类会急剧膨胀。不好维护。
* 怎么解决类爆炸问题?
* 可以使用动态代理来解决这个问题。
* 动态代理还是代理模式,只不过添加了字节码生成技术,可以在内存中为我们
* 动态的生成一个class字节码,这个字节码就是代理类。
* 在内存中动态的生成字节码代理类的技术。叫做:动态代理。
*/
@Override
public void generate(){//目标方法
//模拟生成订单的耗时
try{
Thread.sleep(1234);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("订单已生成.");
}
@Override
public void modify() {//目标方法
//模拟修改订单的耗时
try{
Thread.sleep(456);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("订单已修改.");
}
@Override
public void detail() {//目标方法
//模拟查询订单的耗时
try{
Thread.sleep(111);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("请查看订单详情.");
}
}
测试类Client中的内容:
package com.powernode.proxy.client;
import com.powernode.proxy.service.OrderService;
import com.powernode.proxy.service.OrderServiceImpl;
import java.lang.reflect.Proxy;
public class Client {
//客户端程序
public static void main(String[] args) {
//创建目标对象
OrderService target = new OrderServiceImpl();
//创建代理对象
//三个参数:类加载器,代理类要实现的接口,调用处理器
/*
1.newProxyInstance 翻译为:新建代理对象
也就是说,通过调用这个方法可以创建代理对象。
本质上,这个Proxy.newProxyInstance()方法的执行,做了两件事:
第一件事:再内存中动态的生成了一个代理类的字节码class。
第二件事:new对象了。通过内存中生成的代理类这个代码,实例化了代理对象。
2.关于newProxyInstance()方法的三个重要的参数,每一个什么含义,有什么用?
第一个参数:ClassLoader loader
类加载器。这个类加载器有什么用呢?
再内存当中生成的字节码也是class文件,要执行也得先加载到内存当中。
加载类就需要类加载器,所以这里需要指定类加载器。
并且JDK要求,目标类的类加载器必须和代理类的类加载器使用同一个。
第二个参数:Class<?>[] interfaces
第三个参数:InvocationHandler h
*/
Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),)
//调用代理对象的代理方法
}
}
P96 END
P97 GoF代理模式之JDK动态代理的调用处理器 start:
其他文件中的内容与上集相同
TimerInvocationHandler类中的内容:
package com.powernode.proxy.service;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 专门负责计时的一个调用处理器对象。
* 在这个调用处理器当中编写计时相关的增强代码。
* 这个调用处理器只需要写一个就行了。
*/
public class TimerInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
}
Client客户端程序中的 内容:
package com.powernode.proxy.client;
import com.powernode.proxy.service.OrderService;
import com.powernode.proxy.service.OrderServiceImpl;
import com.powernode.proxy.service.TimerInvocationHandler;
import java.lang.reflect.Proxy;
public class Client {
//客户端程序
public static void main(String[] args) {
//创建目标对象
OrderService target = new OrderServiceImpl();
//创建代理对象
//三个参数:类加载器,代理类要实现的接口,调用处理器
/*
1.newProxyInstance 翻译为:新建代理对象
也就是说,通过调用这个方法可以创建代理对象。
本质上,这个Proxy.newProxyInstance()方法的执行,做了两件事:
第一件事:再内存中动态的生成了一个代理类的字节码class。
第二件事:new对象了。通过内存中生成的代理类这个代码,实例化了代理对象。
2.关于newProxyInstance()方法的三个重要的参数,每一个什么含义,有什么用?
第一个参数:ClassLoader loader
类加载器。这个类加载器有什么用呢?
再内存当中生成的字节码也是class文件,要执行也得先加载到内存当中。
加载类就需要类加载器,所以这里需要指定类加载器。
并且JDK要求,目标类的类加载器必须和代理类的类加载器使用同一个。
第二个参数:Class<?>[] interfaces
代理类和目标类要实现同一个接口或同一些接口。
在内存中生成代理类的时候,这个代理类是需要你告诉它实现哪些接口的。
第三个参数:InvocationHandler h
InvocationHandler 被翻译为:调用处理器。是一个接口。
在调用处理器接口中编写的就是:增强代码。
因为具体要增强什么代码,JDK动态代理技术它是猜不到的。没有那么神。
既然是接口,就要写接口的实现类。
可能会有疑问?
自己还要动手写调用处理器接口的实现类,这不会类爆炸吗?不会。
因为这种调用处理器写一次就好。
*/
Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
new TimerInvocationHandler());
//调用代理对象的代理方法
}
}
P97 end
P98 GoF代理模式之JDK动态代理invoke方法
在java语言中,接口有个特点,当我们的一个类实现了一个接口的时候,就 要求我们必须实现接口里的方法。
本集 其他文件内容和上集相同
TimerInvocationHandler类中的内容:
package com.powernode.proxy.service;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 专门负责计时的一个调用处理器对象。
* 在这个调用处理器当中编写计时相关的增强代码。
* 这个调用处理器只需要写一个就行了。
*/
public class TimerInvocationHandler implements InvocationHandler {
/*
1。为什么强行要求你必须实现InvocationHandler接口?
因为一个类实现接口就必须实现接口中的方法。
以下这个方法必须是invoke(),因为JDK在低层调用invoke()方法的程序已经提前写好了。
注意:invoke方法不是我们程序员负责调用的,是JDK负责调用的。
2.invoke方法什么时候被调用呢?
当代理对象调用代理方法的时候,注册在InvocationHandler调用处理器当中的invoke()方法被调用。
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//这个接口的目的就是为了让你有地方写增强代码。
System.out.println("invoke.....");
return null;
}
}
客户端Client类中的内容:
package com.powernode.proxy.client;
import com.powernode.proxy.service.OrderService;
import com.powernode.proxy.service.OrderServiceImpl;
import com.powernode.proxy.service.TimerInvocationHandler;
import java.lang.reflect.Proxy;
public class Client {
//客户端程序
public static void main(String[] args) {
//创建目标对象
OrderService target = new OrderServiceImpl();
//创建代理对象
//三个参数:类加载器,代理类要实现的接口,调用处理器
/*
1.newProxyInstance 翻译为:新建代理对象
也就是说,通过调用这个方法可以创建代理对象。
本质上,这个Proxy.newProxyInstance()方法的执行,做了两件事:
第一件事:再内存中动态的生成了一个代理类的字节码class。
第二件事:new对象了。通过内存中生成的代理类这个代码,实例化了代理对象。
2.关于newProxyInstance()方法的三个重要的参数,每一个什么含义,有什么用?
第一个参数:ClassLoader loader
类加载器。这个类加载器有什么用呢?
再内存当中生成的字节码也是class文件,要执行也得先加载到内存当中。
加载类就需要类加载器,所以这里需要指定类加载器。
并且JDK要求,目标类的类加载器必须和代理类的类加载器使用同一个。
第二个参数:Class<?>[] interfaces
代理类和目标类要实现同一个接口或同一些接口。
在内存中生成代理类的时候,这个代理类是需要你告诉它实现哪些接口的。
第三个参数:InvocationHandler h
InvocationHandler 被翻译为:调用处理器。是一个接口。
在调用处理器接口中编写的就是:增强代码。
因为具体要增强什么代码,JDK动态代理技术它是猜不到的。没有那么神。
既然是接口,就要写接口的实现类。
可能会有疑问?
自己还要动手写调用处理器接口的实现类,这不会类爆炸吗?不会。
因为这种调用处理器写一次就好。
注意:代理对象和目标对象实现的接口一样,所以可以向下转型。
*/
OrderService proxyObj = (OrderService)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
new TimerInvocationHandler());
//调用代理对象的代理方法
//注意:调用代理对象的代理方法的时候,如果你要做增强的话,目标对象的目标方法得保证执行。
proxyObj.generate();
proxyObj.modify();
proxyObj.detail();
}
}
P98 END
P99 GoF代理模式之invoke方法的三个参数 start:
其它文件内容与上集文件内容相同
TimerInvocationHandler类中的内容:
package com.powernode.proxy.service;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 专门负责计时的一个调用处理器对象。
* 在这个调用处理器当中编写计时相关的增强代码。
* 这个调用处理器只需要写一个就行了。
*/
public class TimerInvocationHandler implements InvocationHandler {
//目标对象
private Object target;
public TimerInvocationHandler(Object target) {
//赋值给成员变量
this.target=target;
}
/*
1。为什么强行要求你必须实现InvocationHandler接口?
因为一个类实现接口就必须实现接口中的方法。
以下这个方法必须是invoke(),因为JDK在低层调用invoke()方法的程序已经提前写好了。
注意:invoke方法不是我们程序员负责调用的,是JDK负责调用的。
2.invoke方法什么时候被调用呢?
当代理对象调用代理方法的时候,注册在InvocationHandler调用处理器当中的invoke()方法被调用。
3.invoke方法的三个参数:
invoke方法是JDK负责调用的,所以JDK调用这个方法的时候会自动给我们传过来这三个参数。
我们可以在invoke方法的大括号中直接使用。
第一个参数:Object proxy 代理对象的引用,这个参数使用较少
第二个参数:Method method 目标对象上的目标方法。(要执行的目标方法就是它。)
第三个参数: Object[] args 目标方法上的实参。
invoke方法执行过程中,使用method来调用目标对象的目标方法。
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//这个接口的目的就是为了让你有地方写增强代码。
System.out.println("增强1");
// 调用目标对象上的目标方法
// 方法四要素:哪个对象,哪个方法,传什么参数,返回什么值
Object retValue = method.invoke(target, args);
System.out.println("增强2");
return null;
}
}
Client客户端程序中的 内容:
package com.powernode.proxy.client;
import com.powernode.proxy.service.OrderService;
import com.powernode.proxy.service.OrderServiceImpl;
import com.powernode.proxy.service.TimerInvocationHandler;
import java.lang.reflect.Proxy;
public class Client {
//客户端程序
public static void main(String[] args) {
//创建目标对象
OrderService target = new OrderServiceImpl();
//创建代理对象
//三个参数:类加载器,代理类要实现的接口,调用处理器
/*
1.newProxyInstance 翻译为:新建代理对象
也就是说,通过调用这个方法可以创建代理对象。
本质上,这个Proxy.newProxyInstance()方法的执行,做了两件事:
第一件事:再内存中动态的生成了一个代理类的字节码class。
第二件事:new对象了。通过内存中生成的代理类这个代码,实例化了代理对象。
2.关于newProxyInstance()方法的三个重要的参数,每一个什么含义,有什么用?
第一个参数:ClassLoader loader
类加载器。这个类加载器有什么用呢?
再内存当中生成的字节码也是class文件,要执行也得先加载到内存当中。
加载类就需要类加载器,所以这里需要指定类加载器。
并且JDK要求,目标类的类加载器必须和代理类的类加载器使用同一个。
第二个参数:Class<?>[] interfaces
代理类和目标类要实现同一个接口或同一些接口。
在内存中生成代理类的时候,这个代理类是需要你告诉它实现哪些接口的。
第三个参数:InvocationHandler h
InvocationHandler 被翻译为:调用处理器。是一个接口。
在调用处理器接口中编写的就是:增强代码。
因为具体要增强什么代码,JDK动态代理技术它是猜不到的。没有那么神。
既然是接口,就要写接口的实现类。
可能会有疑问?
自己还要动手写调用处理器接口的实现类,这不会类爆炸吗?不会。
因为这种调用处理器写一次就好。
注意:代理对象和目标对象实现的接口一样,所以可以向下转型。
*/
OrderService proxyObj = (OrderService)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
new TimerInvocationHandler(target));
//调用代理对象的代理方法
//注意:调用代理对象的代理方法的时候,如果你要做增强的话,目标对象的目标方法得保证执行。
proxyObj.generate();
// proxyObj.modify();
// proxyObj.detail();
}
}
运行结果:
增强1
订单已生成.
增强2
P99 END
P100 GoF代理模式之invoke方法的返回值 start:
OrderServiceImpl类中的内容:
package com.powernode.proxy.service;
public class OrderServiceImpl implements OrderService{//目标对象
@Override
public String getName() {
System.out.println("getName()方法执行了");
return "张三";
}
/**
* 项目经理提出一个新的需求:要统计所有业务接口中每一个业务方法的耗时。
* 解决方案一:硬编码,在每一个业务接口中的每一个业务方法中直接添加统计耗时的程序。
* 这种方案的缺点:
* 缺点一:违背OCP开闭原则。
* 缺点二:代码没有得到复用。(相同的代码写了很多遍)
* 解决方案二:编写业务类的子类,让子类继承业务类,对每个业务方法进行重写。
* 缺点一:虽然解决了OCP开闭原则。但是这种方式会导致耦合度很高,因为采用了继承关系。
* 继承关系是一种耦合度非常高的关系,不建议使用。
* 缺点二:代码没有得到复用。(相同的代码写了很多遍)
* 解决方案三:代理模式。
* 优点1:解决了OCP问题。
* 有点2:采用代理模式的has a,可以降低耦合度。
*
* 目前我们使用的是静态代理,这个静态代理的缺点是什么?
* 类爆炸。假设系统中有1000个接口,那么每个接口都需要代理类,
* 这样类会急剧膨胀。不好维护。
* 怎么解决类爆炸问题?
* 可以使用动态代理来解决这个问题。
* 动态代理还是代理模式,只不过添加了字节码生成技术,可以在内存中为我们
* 动态的生成一个class字节码,这个字节码就是代理类。
* 在内存中动态的生成字节码代理类的技术。叫做:动态代理。
*/
@Override
public void generate(){//目标方法
//模拟生成订单的耗时
try{
Thread.sleep(1234);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("订单已生成.");
}
@Override
public void modify() {//目标方法
//模拟修改订单的耗时
try{
Thread.sleep(456);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("订单已修改.");
}
@Override
public void detail() {//目标方法
//模拟查询订单的耗时
try{
Thread.sleep(111);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("请查看订单详情.");
}
}
OrderService接口中的内容:
package com.powernode.proxy.service;
/**
* 订单业务接口
*/
public interface OrderService {//代理对象和目标对象的公共接口
String getName();
/**
* 生成订单
*/
void generate();
/**
* 修改订单信息
*/
void modify();
/**
* 查看订单详情
*/
void detail();
}
TimerInvocationHandler类中的内容:
package com.powernode.proxy.service;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 专门负责计时的一个调用处理器对象。
* 在这个调用处理器当中编写计时相关的增强代码。
* 这个调用处理器只需要写一个就行了。
*/
public class TimerInvocationHandler implements InvocationHandler {
//目标对象
private Object target;
public TimerInvocationHandler(Object target) {
//赋值给成员变量
this.target=target;
}
/*
1。为什么强行要求你必须实现InvocationHandler接口?
因为一个类实现接口就必须实现接口中的方法。
以下这个方法必须是invoke(),因为JDK在低层调用invoke()方法的程序已经提前写好了。
注意:invoke方法不是我们程序员负责调用的,是JDK负责调用的。
2.invoke方法什么时候被调用呢?
当代理对象调用代理方法的时候,注册在InvocationHandler调用处理器当中的invoke()方法被调用。
3.invoke方法的三个参数:
invoke方法是JDK负责调用的,所以JDK调用这个方法的时候会自动给我们传过来这三个参数。
我们可以在invoke方法的大括号中直接使用。
第一个参数:Object proxy 代理对象的引用,这个参数使用较少
第二个参数:Method method 目标对象上的目标方法。(要执行的目标方法就是它。)
第三个参数: Object[] args 目标方法上的实参。
invoke方法执行过程中,使用method来调用目标对象的目标方法。
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//这个接口的目的就是为了让你有地方写增强代码。
// System.out.println("增强1");
long begin = System.currentTimeMillis();
// 调用目标对象上的目标方法
// 方法四要素:哪个对象,哪个方法,传什么参数,返回什么值
Object retValue = method.invoke(target, args);
// System.out.println("增强2");
long end = System.currentTimeMillis();
System.out.println("耗时"+(end - begin)+"毫秒");
//注意这个invoke方法的返回值,如果代理对象调用代理方法之后,需要返回结果的话
//invoke方法必须将目标对象的目标方法执行结果继续返回。
return retValue;
}
}
Client客户端类中的内容:
package com.powernode.proxy.client;
import com.powernode.proxy.service.OrderService;
import com.powernode.proxy.service.OrderServiceImpl;
import com.powernode.proxy.service.TimerInvocationHandler;
import java.lang.reflect.Proxy;
public class Client {
//客户端程序
public static void main(String[] args) {
//创建目标对象
OrderService target = new OrderServiceImpl();
//创建代理对象
//三个参数:类加载器,代理类要实现的接口,调用处理器
/*
1.newProxyInstance 翻译为:新建代理对象
也就是说,通过调用这个方法可以创建代理对象。
本质上,这个Proxy.newProxyInstance()方法的执行,做了两件事:
第一件事:再内存中动态的生成了一个代理类的字节码class。
第二件事:new对象了。通过内存中生成的代理类这个代码,实例化了代理对象。
2.关于newProxyInstance()方法的三个重要的参数,每一个什么含义,有什么用?
第一个参数:ClassLoader loader
类加载器。这个类加载器有什么用呢?
再内存当中生成的字节码也是class文件,要执行也得先加载到内存当中。
加载类就需要类加载器,所以这里需要指定类加载器。
并且JDK要求,目标类的类加载器必须和代理类的类加载器使用同一个。
第二个参数:Class<?>[] interfaces
代理类和目标类要实现同一个接口或同一些接口。
在内存中生成代理类的时候,这个代理类是需要你告诉它实现哪些接口的。
第三个参数:InvocationHandler h
InvocationHandler 被翻译为:调用处理器。是一个接口。
在调用处理器接口中编写的就是:增强代码。
因为具体要增强什么代码,JDK动态代理技术它是猜不到的。没有那么神。
既然是接口,就要写接口的实现类。
可能会有疑问?
自己还要动手写调用处理器接口的实现类,这不会类爆炸吗?不会。
因为这种调用处理器写一次就好。
注意:代理对象和目标对象实现的接口一样,所以可以向下转型。
*/
OrderService proxyObj = (OrderService)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
new TimerInvocationHandler(target));
//调用代理对象的代理方法
//注意:调用代理对象的代理方法的时候,如果你要做增强的话,目标对象的目标方法得保证执行。
proxyObj.generate();
proxyObj.modify();
proxyObj.detail();
String name = proxyObj.getName();
System.out.println(name);
}
}
运行结果:
订单已生成.
耗时1235毫秒
订单已修改.
耗时458毫秒
请查看订单详情.
耗时113毫秒
getName()方法执行了
耗时0毫秒
张三
P100 end
P101 GoF代理模式之JDK动态代理工具类封装
P101 start :
Time:4h
视频网址:
https://www.bilibili.com/video/BV1Ft4y1g7Fb?p=100&vd_source=ff898217e3612fcbdefecfdc49657ab1