自己编写IOC控制反转及AOP面向切面

网友投稿 1020 2023-01-06

自己编写IOC控制反转及AOP面向切面

自己编写IOC控制反转及AOP面向切面

1.概念

IOC:Inversion of control 控制反转

控制:指的是对象创建(实例化、管理)的权利

反转:控制权交给外部环境了(spring框架、IoC容器)

传统开发⽅式:⽐如类A依赖于类B,往往会在类A中new⼀个B的对象

IoC思想下开发方式:我们不⽤⾃⼰去new对象了,⽽是由IoC容器(Spring框架)去帮助我们实例化对象并且管理它,我们需要使⽤哪个对象,去问IoC容器要即可。

解决的问题:解决对象之间的耦合问题,避免new关键字

Ioc和DI区别:IOC和DI是从不同角度描述同一件事情(对象实例化及依赖关系维护这件事情)。IOC是站在对象的角度,对象实例化及其管理的权力交给了(反转)容器。DI:Dependancy Injection(依赖注⼊),是站在容器的角度,容器会把对象依赖的其他对象注入(送进去),比如A对象实例化过程中因为声明了一个B类型的属性,那么就需要容器把B对象注入给A

AOP:Aspect oriented Programming 面向切面编程

起源:aop是oop的延续,oop三大特征:封装、继承、多态。是一种垂直纵向的继承体系。OOP编程思想可以解决⼤多数的代码重复问题,但是有⼀些情况是处理不了的,⽐如在顶级⽗类中的多个⽅法中相同位置出现了重复代码,OOP就解决不了

横切逻辑代码问题:1.横切代码重复问题。2.横切逻辑代码和业务代码混杂在⼀起,代码臃肿,维护不⽅便

AOP解决的问题:在不改变原有业务逻辑情况下,增强横切逻辑代码,根本上解耦合,避免横切逻辑代码重复

面向切面编程理解:「切」:指的是横切逻辑,原有业务逻辑代码我们不能动,只能操作横切逻辑代码,所以⾯向横切逻辑。「⾯」:横切逻辑代码往往要影响的是很多个⽅法,每⼀个⽅法都如同⼀个点,多个点构成⾯,有⼀个⾯的概念在⾥⾯

2.通过银行转账案例手写IOC和AOP

2.1.表结构

CREATE TABLE `account` (

`name` varchar(255) DEFAULT NULL COMMENT '用户名',

`money` varchar(255) DEFAULT NULL COMMENT '账户金额',

`cardNo` varchar(255) NOT NULL COMMENT '银行卡号'

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2.2.银行转账调用关系

2.3.分析存在的问题

(1)问题⼀:在上述案例实现中,service 层实现类在使⽤ dao 层对象时,直接在TransferServiceImpl 中通过 AccountDao accountDao = new JdbcAccountDaoImpl() 获得了 dao层对象,然⽽⼀个 new 关键字却将 TransferServiceImpl 和 dao 层具体的⼀个实现类JdbcAccountDaoImpl 耦合在了⼀起,如果说技术架构发⽣⼀些变动,dao 层的实现要使⽤其它技术,⽐如 Mybatis,思考切换起来的成本?每⼀个 new 的地⽅都需要修改源代码,重新编译,⾯向接⼝开发的意义将⼤打折扣?

(2)问题⼆:service 层代码没有竟然还没有进⾏事务控制 ?!如果转账过程中出现异常,将可能导致数据库数据错乱,后果可能会很严重,尤其在⾦融业务

2.4.解决问题思路

实例化对象的⽅式除了 new 之外,还有什么技术?反射 (需要把类的全限定类名配置在xml中)

考虑使⽤设计模式中的⼯⼚模式解耦合,另外项⽬中往往有很多对象需要实例化,那就在⼯⼚中使⽤反 射技术实例化对象,⼯⼚模式很合适

service 层没有添加事务控制,怎么办?没有事务就添加上事务控制,⼿动控制 JDBC 的Connection 事务,但要注意将Connection和当前线程绑定(即保证⼀个线程只有⼀个Connection,这样操作才针对的是同⼀个 Connection,进⽽控制的是同⼀个事务)。分析:数据库的事务归根结底是Connection的事务connection.commit();提交事务 connection.rollback();回滚事务

2.5.通过IOC及AOP进行改造

2.5.0.pom.xml

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4.0.0

com.lagou.edu

lagou-transfer

1.0-SNAPSHOT

war

lagou-transfer Maven WebAPP

http://example.com

UTF-8

11

11

junit

junit

4.12

mysql

mysql-connector-java

5.1.35

com.alibaba

druid

1.1.21

javax.servlet

javax.servlet-api

3.1.0

provided

com.fasterxml.jackson.core

jackson-databind

2.9.6

dom4j

dom4j

1.6.1

jaxen

jaxen

1.1.6

cglib

cglib

2.1_2

org.apache.maven.plugins

maven-compiler-plugin

3.2

11

11

UTF-8

org.apache.tomcat.maven

tomcat7-maven-plugin

2.2

8080

/

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4.0.0

com.lagou.edu

lagou-transfer

1.0-SNAPSHOT

war

lagou-transfer Maven Webapp

http://example.com

UTF-8

11

11

junit

junit

4.12

mysql

mysql-connector-java

5.1.35

com.alibaba

druid

1.1.21

javax.servlet

javax.servlet-api

3.1.0

provided

com.fasterxml.jackson.core

jackson-databind

2.9.6

dom4j

dom4j

1.6.1

jaxen

jaxen

1.1.6

cglib

cglib

2.1_2

org.apache.maven.plugins

maven-compiler-plugin

3.2

11

11

UTF-8

org.apache.tomcat.maven

tomcat7-maven-plugin

2.2

8080

/

2.5.1.index.xmhttp://l

转 出

2.5.2.beans.xml

2.5.3.工具类

ConnectionUtils

package com.lagou.edu.utils;

import com.alibaba.druid.pool.DruidPooledConnection;

import java.sql.Connection;

import java.sql.SQLException;

/**

* 获取数据库连接工具类

*/

public class ConnectionUtils {

/* private ConnectionUtils(){}

public static ConnectionUtils getInstance(){

return new ConnectionUtils();

}*/

// 1.单例,保证线程获取到的连接是同一个。(每次新new ConnectionUtils,那么里面的threadlocal也是新的,connection也是新的)

private ThreadLocal threadLocal = new ThreadLocal<>();

public Connection getCurrentThreadConn() throws SQLException {

Connection connection = threadLocal.get();

if (connection == null){

connection = DruidUtils.getInstance().getConnection();

// 创建完成后一定要设置回去

threadLocal.set(connection);

}

return connection;

}

}

DruidUtils

package com.lagou.edu.utils;

import com.alibaba.druid.pool.DruidDataSource;

public class DruidUtils {

private DruidUtils(){

}

private static DruidDataSource druidDataSource = new DruidDataSource();

static {

druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");

druidDataSource.setUrl("jdbc:mysql://localhost:3306/bank");

druidDataSource.setUsername("root");

druidDataSource.setPassword("123456");

}

public static DruidDataSource getInstance() {

return druidDataSource;

}

}

TransactionManager

package com.lagou.edu.utils;

import java.sql.Connection;

import java.sql.SQLException;

/**

* 事务管理器

*/

public class TransactionManager {

private ConnectionUtils connectionUtils;

public void setConnectionUtils(ConnectionUtils connectionUtils) {

this.connectionUtils = connectionUtils;

}

/* private TransactionManager(){}

private static TransactionManager transactionManager = new TransactionManager();

public static TransactionManager getInstance(){

return transactionManager;

}*/

public void beginTranscation() throws SQLException {

Connection conn = connectionUtils.getCurrentThreadConn();

conn.setAutoCommit(false);

System.out.println(conn.getAutoCommit() + ":开启事务的连接:"+conn);

}

public void commit() throws SQLException {

connectionUtils.getCurrentThreadConn().commit();

System.out.println("提交的连接:"+connectionUtils.getCurrentThreadConn());

}

public void rollback() throws SQLException {

connectionUtils.getCurrentThreadConn().rollback();

System.out.println("回滚的连接:"+connectionUtils.getCurrentThreadConn());

}

}

JsonUtils

package com.lagou.edu.utils;

import java.util.List;

import com.fasterxml.jackson.core.JsonProcessingException;

import com.fasterxml.jackson.databind.JavaType;

import com.fasterxml.jackson.databind.ObjectMapper;

/**

* JSON工具类(使用的是jackson实现的)

*/

public class JsonUtils {

private static final ObjectMapper MAPPER = new ObjectMapper();

/**

* 将对象转换成json字符串。

* @param data

* @return

*/

public static String object2Json(Object data) {

try {

String string = MAPPER.writeValueAsString(data);

return string;

} catch (JsonProcessingException e) {

e.printStackTrace();

}

return null;

}

/**

* 将json结果集转化为对象

*

* @param jsonData json数据

* @param beanType 对象中的object类型

* @return

*/

public static T json2Pojo(String jsonData, Class beanType) {

try {

T t = MAPPER.readValue(jsonData, beanType);

return t;

} catch (Exception e) {

e.printStackTrace();

}

return null;

}

/**

* 将json数据转换成pojo对象list

* @param jsonData

* @param beanType

* @return

*/

public static List json2List(String jsonData, Class beanType) {

JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, beanType);

try {

List list = MAPPER.readValue(jsonData, javaType);

return list;

} catch (Exception e) {

e.printStackTrace();

}

return null;

}

}

2.5.4.pojo

Account

package com.lagou.edu.pojo;

public class Account {

private String cardNo;

private String name;

private int money;

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public int getMoney() {

return money;

}

public void setMoney(int money) {

this.money = money;

}

public String getCardNo() { return cardNo; }

public void setCardNo(String cardNo) { this.cardNo = cardNo;}

@Override

public String toString() {

return "Account{" +

"cardNo='" + cardNo + '\'' +

", name='" + name + '\'' +

", money=" + money +

'}';

}

}

2.5.5.工厂类

BeanFactory

package com.lagou.edu.factory;

import org.dom4j.DocumentException;

import org.dom4j.Element;

import org.dom4j.io.SAXReader;

import java.io.InputStream;

import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

/**

* 工厂类

* 作用1:解析xml文件,使用反射技术实例化bean对象,放入map中待用;

* 作用2:提供接口方法根据id从map中获取bean(静态方法)

*

*/

public class BeanFactory {

private static Map map = new HashMap<>();

// 0.服务一启动,就将对象加载至容器中,这里使用静态代码块

static{

// 1.解析对象配置文件

InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");

try {

// 使用dom4j技术,首先获取根节点

Element rootElement = new SAXReader().read(resourceAsStream).getRootElement();

// 使用xpath,寻找 节点

List beanList = rootElement.selectNodes("//bean");

for (Element element : beanList) {

String id = element.attributeValue("id");

String aClass = element.attributeValue("class");

// 2.使用反射技术,根据类的全路径创建对象

Class> aClass1 = Class.forName(aClass);

Object o = aClass1.newInstance();

// 3.将解析后的对象放入容器中(map)

map.put(id,o);

}

// 遍历property标签,将属性注入,维护bean之间的依赖关系

List propertyList = rootElement.selectNodes("//property");

for (Element element : propertyList) {

String name = element.attributeValue("name");

String ref = element.attributeValue("ref");

// 使用反射技术,设置属性

Element parent = element.getParent();

String parentId = parent.attributeValue("id");

Object parentObj = map.get(parentId);

Method[] methods = parentObj.getClass().getMethods();

// 获取所有方法,寻找set+name,将ref设置

for (Method method : methods) {

if(method.getName().equalsIgnoreCase("set"+name)){

Object propertyObj = map.get(ref);

method.invoke(parentObj,propertyObj);

}

}

// 维护依赖关系后重新将bean放入map中

map.put(parentId,parentObj);

}

} catch (DocumentException | ClassNotFoundException e) {

e.printStackTrace();

} catch (IllegalAccessException e) {

e.printStackTrace();

} catch (InstantiationException e) {

e.printStackTrace();

} catch (InvocationTargetException e) {

e.printStackTrace();

}

}

// 3.提供获取对象的方法

public static Object getBean(String id){

return map.get(id);

}

}

ProxyFactory

package com.lagou.edu.factory;

import com.lagou.edu.utils.TransactionManager;

import net.sf.cglib.proxy.Enhancer;

import net.sf.cglib.proxy.MethodInterceptor;

import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

/**

*

* 代理对象工厂:生成代理对象的

*/

public class ProxyFactory {

private TransactionManager transactionManager;

public void setTransactionManager(TransactionManager transactionManager) {

this.transactionManager = transactionManager;

}

/*private ProxyFactory(){

}

private static ProxyFactory proxyFactory = new ProxyFactory();

public static ProxyFactory getInstance() {

return proxyFactory;

}*/

/**

* Jdk动态代理

* @param obj 委托对象

* @return 代理对象

*/

public Object getJdkProxy(Object obj) {

// 获取代理对象

return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),

new InvocationHandler() {

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

Object result = null;

try{

// 开启事务(关闭事务的自动提交)

transactionManager.beginTranscation();

result = method.invoke(obj,args);

// 提交事务

transactionManager.commit();

}catch (Exception e) {

e.printStackTrace();

// 回滚事务

transactionManager.rollback();

// 抛出异常便于上层servlet捕获

throw e;

}

return result;

}

});

}

/**

* 使用cglib动态代理生成代理对象

* @param obj 委托对象

* @return

*/

public Object getCglibProxy(Object obj) {

return Enhancer.create(obj.getClass(), new MethodInterceptor() {

@Override

public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

Object result = null;

try{

// 开启事务(关闭事务的自动提交)

transactionManager.beginTranscation();

result = method.invoke(obj,objects);

// 提交事务

transactionManager.commit();

}catch (Exception e) {

// 回滚事务

transactionManager.rollback();

e.printStackTrace();

// 抛出异常便于上层servlet捕获

throw e;

}

return result;

}

});

}

}

2.5.6.dao层

AccountDao

package com.lagou.edu.dao;

import com.lagou.edu.pojo.Account;

public interface AccountDao {

Account queryAccountByCardNo(String cardNo) throws Exception;

int updateAccountByCardNo(Account account) throws Exception;

}

JdbcAccountDaoImpl

package com.lagou.edu.dao.impl;

import com.lagou.edu.pojo.Account;

import com.lagou.edu.dao.AccountDao;

import com.lagou.edu.utils.ConnectionUtils;

import com.lagou.edu.utils.DruidUtils;

import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

public class JdbcAccountDaoImpl implements AccountDao {

private ConnectionUtils connectionUtils;

public void setConnectionUtils(ConnectionUtils connectionUtils) {

this.connectionUtils = connectionUtils;

}

public void init() {

System.out.println("初始化方法.....");

}

public void destory() {

System.out.println("销毁方法......");

}

@Override

public Account queryAccountByCardNo(String cardNo) throws Exception {

//从连接池获取连接

// Connection con = DruidUtils.getInstance().getConnection();

// 从当前线程中获取连接池对象

Connection con = connectionUtils.getCurrentThreadConn();

String sql = "select * from account where cardNo=?";

PreparedStatement preparedStatement = con.prepareStatement(sql);

preparedStatement.setString(1,cardNo);

ResultSet resultSet = preparedStatement.executeQuery();

Account account = new Account();

while(resultSet.next()) {

account.setCardNo(resultSet.getString("cardNo"));

account.setName(resultSet.getString("name"));

account.setMoney(resultSet.getInt("money"));

}

resultSet.close();

preparedStatement.close();

// con.close(); // 不能将当前线程的连接关闭了,不然同个线程同个业务中其他更新方法获取的连接就不是同一个

return account;

}

@Override

public int updateAccountByCardNo(Account account) throws Exception {

// 从连接池获取连接

// 改造为:从当前线程当中获取绑定的connection连接

// Connection con = DruidUtils.getInstance().getConnection();

// 从当前线程中获取连接池对象

Connection con = connectionUtils.getCurrentThreadConn();

String sql = "update account set money=? where cardNo=?";

PreparedStatement preparedStatement = con.prepareStatement(sql);

preparedStatement.setInt(1,account.getMoney());

preparedStatement.setString(2,account.getCardNo());

int i = preparedStatement.executeUpdate();

preparedStatement.close();

// con.close();

return i;

}

}

2.5.7.service层

TransferService

package com.lagou.edu.service;

public interface TransferService {

void transfer(String fromCardNo,String toCardNo,int money) throws Exception;

}

TransferServiceImpl

package com.lagou.edu.service.impl;

import com.lagou.edu.dao.AccountDao;

import com.lagou.edu.pojo.Account;

import com.lagou.edu.service.TransferService;

import com.lagou.edu.utils.TransactionManager;

public class TransferServiceImpl implements TransferService {

// private AccountDao accountDao = new JdbcAccountDaoImpl();

// private AccountDao accountDao = (AccountDao) BeanFactory.getBean("accountDao");

// 最佳状态

private AccountDao accountDao;

// 构造函数传值/set方法传值

public void setAccountDao(AccountDao accountDao) {

this.accountDao = accountDao;

}

@Override

public void transfer(String fromCardNo, String toCardNo, int money) throws Exception {

// try {

// // 开启事务(设置自动提交关闭)

// TransactionManager.getInstance().beginTranscation();

Account from = accountDao.queryAccountByCardNo(fromCardNo);

Account to = accountDao.queryAccountByCardNo(toCardNo);

from.setMoney(from.getMoney()-money);

to.setMoney(to.getMoney()+money);

accountDao.updateAccountByCardNo(to);

int c = 1/0;

accountDao.updateAccountByCardNo(from);

// 事务提交

// TransactionManager.getInstance().commit();

// }catch (Exception e){

// // 事务回滚

// TransactionManager.getInstance().rollback();

// throw e;

// }

}

}

2.5.8.controller层

TransferServlet

package com.lagou.edu.servlet;

import com.lagou.edu.factory.BeanFactory;

import com.lagou.edu.factory.ProxyFactory;

import com.lagou.edu.service.impl.TransferServiceImpl;

import com.lagou.edu.utils.JsonUtils;

import com.lagou.edu.pojo.Result;

import com.lagou.edu.service.TransferService;

import javax.servlet.ServletException;

import javax.servlet.annotation.WebServlet;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import java.io.IOException;

@WebServlet(name="transferServlet",urlPatterns = "/transferServlet")

public class TransferServlet extends HttpServlet {

// 1. 实例化service层对象

// private TransferService transferService = new TransferServiceImpl();

// private TransferService transferService = (TransferService) BeanFactory.getBean("transferService");

// 从工厂获取委托对象,使用代理对象,主要增加了事务控制

private ProxyFactory proxyFactory = (ProxyFactory) BeanFactory.getBean("proxyFactory");

private TransferService transferService = (TransferService) proxyFactory.getJdkProxy((TransferService) BeanFactory.getBean("transferService"));

@Override

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

doPost(req,resp);

}

@Override

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

// 设置请求体的字符编码

req.setCharacterEncoding("UTF-8");

String fromCardNo = req.getParameter("fromCardNo");

String toCardNo = req.getParameter("toCardNo");

String moneyStr = req.getParameter("money");

int money = Integer.parseInt(moneyStr);

Result result = new Result();

try {

// 2. 调用service层方法

transferService.transfer(fromCardNo,toCardNo,money);

result.setStatus("200");

} catch (Exception e) {

e.printStackTrace();

result.setStatus("201");

result.setMessage(e.toString());

}

// 响应

resp.setContentType("application/json;charset=utf-8");

resp.getWriter().print(JsonUtils.object2Json(result));

}

}

2.5.9.注意事项

com.lagou.edu.utils.ConnectionUtils#getCurrentThreadConn中一定要注意,第一次获取连接为空时,创建连接后要设置到当前线程中。

if (connection == null){

connection = DruidUtils.getInstance().getConnection();

// 创建完成后一定要设置回去

threadLocal.set(connection);

}

总结

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:计算机移动应用开发是什么(计算机移动应用开发是什么工作)
下一篇:上饶企业app开发要多久(上饶企业app开发要多久完成)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~