在java中,在不改变源代码的情况下,实现方法增强的方式有三种:
- 1,继承
- 2,装饰者模式
- 3,代理模式(静态代理和动态代理)
1,继承模式:
- 简单来说,就是通过继承的方式,在子类方法中添加相应的增强方法,然后通过调用子类方法来实现增强。
//继承比较简单,直接继承,重写中调用父类方法即可
2,装饰者模式:
- 装饰者模式是真正完全不改变源码的情况下增强方法的一种方式,即便是调用方式也无需改变
- 这里用自定义的连接池来进行说明
- 在自定义连接池中,如果没有增强过Connection的话,那么是不能按照原来的方式直接调用connection.close();方法的,那么可以通过装饰,把连接池中的Connection对象直接在放入池之前包装成我自定义的Connection,这样从连接池中取出来的连接也是我自定义的连接,那么只需要在自定义的方法中实现close()和prepareStatement等就可以达到直接调用关闭方法的目的,具体如下:
//1,自定义连接池
/*
* 对JDBC连接的封装,也就是自定义连接池
* 其他一些方法也需要重写,但是不需要任何改变,所以这里就没有贴出来
*/
public class JDBCDatasource implements DataSource {
private static LinkedList connections = new LinkedList();
//往连接池中添加连接
static{
for(int i=0;i<5;i++){
Connection connection = JDBCUtil.getConnection();
JDBCConnection theConnection = new JDBCConnection(connections, connection);
connections.add(theConnection);
}
}
//重写这一个方法,如果没有增强过connection的话,需要调用这个方法归还连接到连接池中
@Override
public Connection getConnection() throws SQLException {
if (connections.size() == 0) {
for(int i=0;i<5;i++){
Connection connection = JDBCUtil.getConnection();
JDBCConnection theConnection = new JDBCConnection(connections, connection);
connections.add(theConnection);
}
}
return connections.removeFirst();
}
//新增一个方法
public void returnConnection(Connection connection){
connections.add(connection);
}
}
//2,自定义连接类,实现相应的方法,并在自定义的连接池中进行包装,具体看1中的代码
//其他一些不需要修改的覆盖方法这里不再贴出
public class JDBCConnection implements Connection {
private Connection connection;
private LinkedList connections;
public JDBCConnection(List connections, Connection connection) {
this.connections = (LinkedList) connections;
this.connection = connection;
}
//如果想要在关闭的时候添加到连接池,那么需要把连接池传进来,传进来最好的时候就是创建的时候
@Override
public void close() throws SQLException {
System.out.println("here here!");
connections.add(connection);
}
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
return connection.prepareStatement(sql);
}
}
//测试
JDBCDatasource datasource = new JDBCDatasource();
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
connection = datasource.getConnection();
preparedStatement = connection.prepareStatement("select * from product;");
resultSet = preparedStatement.executeQuery();
while(resultSet.next()){
System.out.println(resultSet.getString("pname"));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
//这行代码中封装了connection.close()方法
JDBCUtil.closeAll(connection, preparedStatement, resultSet);
}
3,代理模式:
- 代理分为动态代理和静态代理,区别就是静态代理是自己创建一个代理类,实现相应的被代理对象的方法,增加相应的增强代码。而动态代理是通过类加载器,反射等在运行时创建代理类,也就是不需要手动创建代理类,在对应的代理方法newProxyInstance中的代码块中直接添加增强代码;
- 动态代理是在静态代理的基础上的拓展,所以先看下静态代理:
//1,首先需要有一个接口类,方便目标对象和代理对象去实现
public interface DogInterface {
public void eat();
public void run();
}
//2,目标对象中实现借口类
public class Dog implements DogInterface {
@Override
public void eat() {
System.out.println("Dog -----eat");
}
@Override
public void run() {
System.out.println("dog----run----");
}
}
//3,创建一个代理对象,在代理对象中实现借口类,并在对应的方法中调用目标对象的方法。
public class DogProxy implements DogInterface {
@Override
public void eat() {
System.out.println("dog在eat前,准备工作代码等");
//这里调用目标对象的方法
Dog dog = new Dog();
dog.eat();
System.out.println("dog在eat后收尾工作代码等");
}
@Override
public void run() {
}
}
//4,实际使用时候使用代理对象即可
public void proxyTest(){
DogProxy proxy = new DogProxy();
proxy.eat();//这里就ok了
}
- 下面是动态代理的代码实现,前两步是完全一致的
有一点需要注意的是:动态代理方法虽然能增强方法,但主要的使用场合是在拦截中进行相应的处理,如在全局的拦截器中进行乱码处理等
//1,首先需要有一个接口类,方便目标对象和代理对象去实现
public interface DogInterface {
public void eat();
public void run();
}
//2,目标对象中实现借口类
public class Dog implements DogInterface {
@Override
public void eat() {
System.out.println("Dog -----eat");
}
@Override
public void run() {
System.out.println("dog----run----");
}
}
//3,在调用的时候使用代理类调用静态方法创建动态代理
public void dynamicProxyTest(){
DogInterface proxy = (DogInterface) Proxy.newProxyInstance(
Dog.class.getClassLoader(),
Dog.class.getInterfaces(), //new Class[]{DogInterface.class}
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
method.invoke(new Dog(), args);
System.out.println("after");
return null;
}
});
proxy.eat();
proxy.run();
}
//搞定!