目录
什么是动态代理?
动态代理的两种方式?
jdk动态代理;
cglib静态代理
为什么需要代理?
1、原有功能增强
2、降低耦合
JDK动态代理代码实例:
实体类:
持久层接口+实现类(使用jdbc进行数据库操作)
业务层接口+实现类(调用持久层,仅书写业务逻辑,不进行其他操作,如添加事物处理等)
代理类:代理的是AccountService接口 获取业务层接口的代理对象,增强后返回
工具类:提供一些工具方法,如建立连接池,创建数据库,进行事物操作的方法等
测试类:直接调用代理类进行操作
什么是动态代理?
动态代理就是,在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术。在生成代理对象的过程中,目标对象不变,代理对象中的方法是目标对象方法的增强方法。
可以通过调用代理对象来实现对目标对象的调用。
动态代理的两种方式?
-
jdk动态代理;
-
cglib静态代理
为什么需要代理?
1、原有功能增强
举例来说,当前类的代码只能满足一些基本的功能,而这些功能满足不了新需求,但又不能改动以前的代码,这时就用代理模式。通过代理类,扩展原有类的功能,客户端访问的入口只是从目标对象切换到代理对象而已;
2、降低耦合
使用代理类,可以在不对原来类修改的基础上,进行相关功能的扩展
JDK动态代理代码实例:
实体类:
/*** 实体类* 账户表的实体类* 序列化* 反序列化*/
public class Account implements Serializable{//idprivate Integer id;//名称private String name;//钱 金额private Double money;public Account() {}public Account(Integer id, String name, Double money) {this.id = id;this.name = name;this.money = money;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Double getMoney() {return money;}public void setMoney(Double money) {this.money = money;}@Overridepublic String toString() {return "Account{" +"id=" + id +", name='" + name + '\'' +", money=" + money +'}';}
}
持久层接口+实现类(使用jdbc进行数据库操作)
/*** 持久层接口* 面向接口开发*/
public interface AccountDao {//保存账户public void saveAll(Account account) throws SQLException;
}
/*** 持久层接口实现类* 面向接口开发*/
public class AccountDaoImpl implements AccountDao{// @Overridepublic void saveAll(Account account) throws SQLException {//jdbc//使用工具类 减少加载驱动 直接获取连接System.out.println("持久层:保存账户...");// 把数据存储到数据库中// 先获取到连接Connection connection = TxUtils.getConnection();//编写sql// 编写sql语句String sql = "insert into account values (null,?,?)";//获取执行对象stmt prepareStatement(sql注入) Statement(不可以防止)PreparedStatement statement = connection.prepareStatement(sql);// 设置值statement.setString(1,account.getName());statement.setDouble(2,account.getMoney());//执行sqlint i = statement.executeUpdate();//关闭资源statement.close();}
}
业务层接口+实现类(调用持久层,仅书写业务逻辑,不进行其他操作,如添加事物处理等)
/*** 账户的接口*/
public interface AccountService {//保存账户public void saveAll(Account account1,Account account2) throws SQLException;
}
/*** 账户业务层实现类* 目的:实现新增账户* 演示事务*/
public class AccountServiceImpl implements AccountService{private AccountDao accountDao;public void setAccountDao(AccountDao accountDao) {this.accountDao = accountDao;}/*** 实现保存* @param account1* @param account2* @throws SQLException*/
// @Overridepublic void saveAll(Account account1, Account account2) throws SQLException {accountDao.saveAll(account1);int i=5/0;accountDao.saveAll(account2);}
}
代理类:代理的是AccountService接口 获取业务层接口的代理对象,增强后返回
因为jdk动态代理是面向接口的
/*** 代理类* 使用动态代理JDK 面向接口* 传入目标对象,生成该对象的代理对象,返回。对目标对象的方法进行增强* Proxy java 反射*/
public class JdkProxy {/*** 获取代理对象并返回,增强目标对象的方法* @param accountService* @return*/public static Object getProxy(final AccountService accountService){//通过java反射包里提供的Proxy的类的api方法newProxyInstance,按要求(根据三个参数)返回一个代理对象proxyObject proxy=Proxy.newProxyInstance(JdkProxy.class.getClassLoader(), accountService.getClass().getInterfaces(), new InvocationHandler() {/*** 调用代理对象AccountServiceImpl的方法,invoke方法就会去执行*/
// @Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//创建一个空的结果集对象Object result=null;try {// 开启事务TxUtils.startTransaction();//执行目标对象的实际业务// 对象目标对象的方法进行增强result = method.invoke(accountService, args);// 提交事务TxUtils.commit();}catch (Exception e){e.printStackTrace();//回滚事务TxUtils.rollback();}finally {TxUtils.close();}return result;//返回代理对象}});return proxy;}
}
工具类:提供一些工具方法,如建立连接池,创建数据库,进行事物操作的方法等
*** 事务的工具类 数据库连接的工具类*/
public class TxUtils {//类属性 连接池对象private static DruidDataSource ds = null;// 使用ThreadLocal存储当前线程中的Connection对象private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();// 在静态代码块中创建数据库连接池static {try {// 通过代码创建druid数据库连接池ds = new DruidDataSource();ds.setDriverClassName("com.mysql.jdbc.Driver");ds.setUrl("jdbc:mysql:///spring_db?useUnicode=true&characterEncoding=utf8");ds.setUsername("root");ds.setPassword("root");} catch (Exception e) {throw new ExceptionInInitializerError(e);}}/*** @Method: getConnection* @Description: 从数据源中获取数据库连接* @Anthor:* @return Connection* @throws SQLException*/public static Connection getConnection() throws SQLException {// 从当前线程中获取ConnectionConnection conn = threadLocal.get();if (conn == null) {// 从数据源中获取数据库连接conn = getDataSource().getConnection();// 将conn绑定到当前线程threadLocal.set(conn);}return conn;}/*** @Method: startTransaction* @Description: 开启事务* @Anthor:**/public static void startTransaction() {try {Connection conn = threadLocal.get();if (conn == null) {conn = getConnection();// 把 conn绑定到当前线程上threadLocal.set(conn);}// 开启事务conn.setAutoCommit(false);} catch (Exception e) {throw new RuntimeException(e);}}/*** @Method: rollback* @Description:回滚事务* @Anthor:*/public static void rollback() {try {// 从当前线程中获取ConnectionConnection conn = threadLocal.get();if (conn != null) {// 回滚事务conn.rollback();}} catch (Exception e) {throw new RuntimeException(e);}}/*** @Method: commit* @Description:提交事务* @Anthor:*/public static void commit() {try {// 从当前线程中获取ConnectionConnection conn = threadLocal.get();if (conn != null) {// 提交事务conn.commit();}} catch (Exception e) {throw new RuntimeException(e);}}/*** @Method: close* @Description:关闭数据库连接(注意,并不是真的关闭,而是把连接还给数据库连接池)* @Anthor:**/public static void close() {try {// 从当前线程中获取ConnectionConnection conn = threadLocal.get();if (conn != null) {conn.close();// 解除当前线程上绑定connthreadLocal.remove();}} catch (Exception e) {throw new RuntimeException(e);}}/*** @Method: getDataSource* @Description: 获取数据源* @Anthor:* @return DataSource*/public static DataSource getDataSource() {// 从数据源中获取数据库连接return ds;}}
测试类:直接调用代理类进行操作
public class TestDemo {@Testpublic void run() throws SQLException {ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");AccountService service = (AccountService) ac.getBean("service");AccountService proxy = (AccountService) JdkProxy.getProxy(service);Account account1=new Account();account1.setName("张三0716");account1.setMoney(5000.00);Account account2=new Account();account2.setName("李四0716");account2.setMoney(9000.00);proxy.saveAll(account1,account2);}
}