app开发者平台在数字化时代的重要性与发展趋势解析
1020
2023-01-06
自己编写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">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
2.5.1.index.xmhttp://l
body {
background-color:#00b38a;
text-align:center;
}
.lp-login {
position:absolute;
width:500px;
height:300px;
top:50%;
left:50%;
margin-top:-250px;
margin-left:-250px;
background: #ffffff;
border-radius: 4px;
box-shadow: 0 0 10px #12a591;
padding: 57px 50px 35px;
box-sizing: border-box
}
.lp-login .submitBtn {
display:block;
text-decoration:none;
height: 48px;
width: 150px;
line-height: 48px;
font-size: 16px;
color: #fff;
text-align: center;
background-image: -webkit-gradient(linear, left top, right top, from(#09cb9d), to(#02b389));
background-image: linear-gradient(90deg, #09cb9d, #02b389);
border-radius: 3px
}
input[type='text'] {
height:30px;
width:250px;
}
span {
font-style: normal;
font-variant-ligatures: normal;
font-variant-caps: normal;
font-variant-numeric: normal;
font-variant-east-asian: normal;
font-weight: normal;
font-stretch: normal;
font-size: 14px;
line-height: 22px;
font-family: "Hiragino Sans GB", "Microsoft Yahei", SimSun, Arial, "Helvetica Neue", Helvetica;
}
$(function(){
$(".submitBtn").bind("click",function(){
var fromAccount = $("#fromAccount").val();
var toAccount = $("#toAccount").val();
var money = $("#money").val();
if(money == null || $.trim(money).length == 0){
alert("sorry,必须输入转账金额~");
return;
}
$.ajax({
url:'/transferServlet',
type:'POST', //GET
async:false, //或false,是否异步
data:{
fromCardNo:fromAccount.split(' ')[1],
toCardNo:toAccount.split(' ')[1],
money:money
},
timeout:5000, //超时时间
dataType:'json', //返回的数据格式:json/xml/html/script/jsonp/text
success:function(data){
if("200" == data.status){
alert("转账成功~~~");
}else{
alert("转账失败~~~,message:" + data.message);
}
}
})
})
})
//检查输入值是否为整数
function checkFormat(obj){
var reg = /^[0-9]+[0-9]*]*$/;
if($.trim($(obj).val()).length>0){
if(!reg.test($(obj).val())){
alert("输入格式错误!请输整数!");
$(obj).val("");
}else{
$(obj).val(parseInt($(obj).val()));
}
}
}
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
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
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
JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, beanType);
try {
List
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
// 0.服务一启动,就将对象加载至容器中,这里使用静态代码块
static{
// 1.解析对象配置文件
InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
try {
// 使用dom4j技术,首先获取根节点
Element rootElement = new SAXReader().read(resourceAsStream).getRootElement();
// 使用xpath,寻找
List
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
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小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~