Spring回顾
作者:互联网
Spring回顾
Spring两个核心思想:IOC和AOP
-
什么是ioc:
-
Inversion Of Control(简称IOC),控制反转;它是一个技术思想,不是一个技术实现;
-
开发模式:我们不用自己去new对象,是通过从IOC容器去帮助我们实例化对象并且管理它,我们需要使用哪个对象,去问IOC容器拿即可;此外,我们失去了创建对象的权力,得到了不用考虑对象的创建和管理等一系列事情;
-
为什么叫做控制反转?
-
控制: 指的是对象创建(实例化,管理)的权利
-
反转:控制反转交给外部环境(如spring.ioc容器)
-
-
IOC和DI的区别:
-
DI:Dependancy Injection(依赖注入) ,IOC和DI描述的是同一件事情(对象实例化及依赖关系维护这件事情),只不过角度不一样罢了,
IOC是站在对象的角度,对象实例化及管理的权利交给了反转容器;DI是站在容器的角度;
-
-
-
什么是AOP
-
AOP: Aspect o'riented Programming 面向切面/方面变成,AOP是OOP的延续,从OOP说起:
-
OOP三大特征:封装,继承,多态;OOP思想是一种垂直纵向的继承体系(联想extends),OOP编程思想可以解决大多数的代码重复问题,但是在顶级父类中的多个方法中相同位置出现了重复代码,OOP就解决不了;这时候AOP就出场了...
-
-
为什么叫做面向切面编程?
-
在多个纵向(顺序)流程中,出现的相同子流程代码,我们称之为横切逻辑代码,横切逻辑代码的使用场景很有限,一般有:事务控制,权限校验,日志等
-
-
spring框架的ioc实现三种方式:
-
纯xml(bean信息定义全部配置在xml中)
-
javaSE应用:ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml")或者new FileSystemXmlApplicationContext("c:/beans.xml")
-
javaWeb应用:ContextLoaderListener(监听器去加载xml)
-
-
xml+注解:部分bean使用注解定义(实现同上)
-
纯注解模式,所有的bean都是用注解来定义
-
javaSE应用:applicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
-
javaWeb应用:ContextLoaderListener(监听器去加载注解配置类)
-
(springf'ramework.beans关系图)
BeanFactory与ApplicationContext区别
BeanFactory 是Spring框架中Ioc容器的顶层接口,它只是用来定义一些基础功能,定义一些基础规范,而 ApplicationContext 是它的一个子接口,所以ApplicationContext 是具备 BeanFactory 提供的全部的功能的;通常我们称 BeanFactory 为SpringIOC的基础容器,ApplicationContext 是容器的高级接口,比 BeanFactory 拥有更多的功能;
手写实现IOC和AOP
上⼀部分我们理解了 IoC 和 AOP 思想,我们先不考虑 Spring 是如何实现这两个思想的,此处准备了⼀ 个『银⾏转账』的案例,请分析该案例在代码层次有什么问题 ?分析之后使⽤我们已有知识解决这些问 题(痛点)。其实这个过程我们就是在⼀步步分析并⼿写实现 IoC 和 AOP。
银行转帐案例界面
表结构
代码调用关系
TransferServlet
@WebServlet(name="transferServlet",urlPatterns = "/transferServlet")
public class TransferServlet extends HttpServlet {
// 1. 实例化service层对象
private TransferService transferService = new TransferServiceImpl();
@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));
}
}
TransferService接⼝及实现类
public class TransferServiceImpl implements TransferService {
private AccountDao accountDao = new JdbcAccountDaoImpl();
@Override
public void transfer(String fromCardNo, String toCardNo, int money)
throws Exception {
Account from = accountDao.queryAccountByCardNo(fromCardNo);
Account to = accountDao.queryAccountByCardNo(toCardNo);
from.setMoney(from.getMoney()-money);
to.setMoney(to.getMoney()+money);
accountDao.updateAccountByCardNo(from);
accountDao.updateAccountByCardNo(to);
}
}
AccountDao层接⼝及基于Jdbc的实现类
public interface AccountDao {
Account queryAccountByCardNo(String cardNo) throws Exception;
int updateAccountByCardNo(Account account) throws Exception;
}
JdbcAccountDaoImpl(Jdbc技术实现Dao层接⼝)
public class JdbcAccountDaoImpl implements AccountDao {
@Override
public Account queryAccountByCardNo(String cardNo) throws Exception {
//从连接池获取连接
Connection con = DruidUtils.getInstance().getConnection();
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 con = DruidUtils.getInstance().getConnection();
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;
}
}
代码问题分析
(1)问题⼀:在上述案例实现中,service 层实现类在使⽤ dao 层对象时,直接在 TransferServiceImpl 中通过 AccountDao accountDao = new JdbcAccountDaoImpl() 获得了 dao层对 象,然⽽⼀个 new 关键字却将 TransferServiceImpl 和 dao 层具体的⼀个实现类 JdbcAccountDaoImpl 耦合在了⼀起,如果说技术架构发⽣⼀些变动,dao 层的实现要使⽤其它技术, ⽐如 Mybatis,思考切换起来的成本?每⼀个 new 的地⽅都需要修改源代码,重新编译,⾯向接⼝开发 的意义将⼤打折扣?
(2)问题⼆:service 层代码没有竟然还没有进⾏事务控制 ?!如果转账过程中出现异常,将可能导致 数据库数据错乱,后果可能会很严重,尤其在⾦融业务。
问题解决思路
-
针对问题⼀思考:
-
实例化对象的⽅式除了 new 之外,还有什么技术?反射 (需要把类的全限定类名配置在xml 中)
-
-
考虑使⽤设计模式中的⼯⼚模式解耦合,另外项⽬中往往有很多对象需要实例化,那就在⼯⼚中使 ⽤反 射技术实例化对象,⼯⼚模式很合适
-
更进⼀步,代码中能否只声明所需实例的接⼝类型,不出现 new 也不出现⼯⼚类的字眼,如下 图? 能!声明⼀个变量并提供 set ⽅法,在反射的时候将所需要的对象注⼊进去
-
针对问题⼆思考:
-
service 层没有添加事务控制,怎么办?没有事务就添加上事务控制,⼿动控制 JDBC 的 Connection 事务,但要注意将Connection和当前线程绑定(即保证⼀个线程只有⼀个 Connection,这样操作才针对的是同⼀个 Connection,进⽽控制的是同⼀个事务)
-
下面我们进行代码改造:
-
针对问题⼀的代码改造
-- beans.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans>
<bean id="transferService"
class="com.yun.test.service.impl.TransferServiceImpl">
<property name="AccountDao" ref="accountDao"></property>
</bean>
<bean id="accountDao"
class="com.yun.test.dao.impl.JdbcAccountDaoImpl">
</bean>
</beans>
-
增加 BeanFactory.java
public class BeanFactory {
/**
* ⼯⼚类的两个任务
* 任务⼀:加载解析xml,读取xml中的bean信息,通过反射技术实例化bean对象,然后放⼊
map待⽤
* 任务⼆:提供接⼝⽅法根据id从map中获取bean(静态⽅法)
*/
private static Map<String,Object> map = new HashMap<>();
static {
InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
SAXReader saxReader = new SAXReader();
try {
Document document = saxReader.read(resourceAsStream);
Element rootElement = document.getRootElement();
List<Element> list = rootElement.selectNodes("//bean");
// 实例化bean对象
for (int i = 0; i < list.size(); i++) {
Element element = list.get(i);
String id = element.attributeValue("id");
String clazz = element.attributeValue("class");
Class<?> aClass = Class.forName(clazz);
Object o = aClass.newInstance();
map.put(id,o);
}
// 维护bean之间的依赖关系
List<Element> propertyNodes = rootElement.selectNodes("//property");
for (int i = 0; i < propertyNodes.size(); i++) {
Element element = propertyNodes.get(i);
// 处理property元素
String name = element.attributeValue("name");
String ref = element.attributeValue("ref");
String parentId = element.getParent().attributeValue("id");
Object parentObject = map.get(parentId);
Method[] methods = parentObject.getClass().getMethods();
for (int j = 0; j < methods.length; j++) {
Method method = methods[j];
if(("set" + name).equalsIgnoreCase(method.getName())){
// bean之间的依赖关系(注⼊bean)
Object propertyObject = map.get(ref);
method.invoke(parentObject,propertyObject);
}
}
// 维护依赖关系后重新将bean放⼊map中
map.put(parentId,parentObject);
}
} catch (DocumentException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
public static Object getBean(String id) {
return map.get(id);
}
}
-
修改 TransferServlet
-
修改 TransferServiceImpl
针对问题⼆的改造
-
增加 ConnectionUtils
public class ConnectionUtils { /*private ConnectionUtils() { } private static ConnectionUtils connectionUtils = new ConnectionUtils(); public static ConnectionUtils getInstance() { return connectionUtils; }*/ private ThreadLocal<Connection> threadLocal = new ThreadLocal<>(); //存储当前线程的连接 /** * 从当前线程获取连接 */ public Connection getCurrentThreadConn() throws SQLException { /** * 判断当前线程中是否已经绑定连接,如果没有绑定,需要从连接池获取⼀个连接绑定到 当前线程 */ Connection connection = threadLocal.get(); if(connection == null) { // 从连接池拿连接并绑定到线程 connection = DruidUtils.getInstance().getConnection(); // 绑定到当前线程 threadLocal.set(connection); } return connection; } }
-
增加 TransactionManager 事务管理器类
public class TransactionManager {
private ConnectionUtils connectionUtils;
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}
// 开启事务
public void beginTransaction() throws SQLException {
connectionUtils.getCurrentThreadConn().setAutoCommit(false);
}
// 提交事务
public void commit() throws SQLException {
connectionUtils.getCurrentThreadConn().commit();
}
// 回滚事务
public void rollback() throws SQLException {
connectionUtils.getCurrentThreadConn().rollback();
}
}
-
增加 ProxyFactory 代理⼯⼚类
public class ProxyFactory {
private TransactionManager transactionManager;
public void setTransactionManager(TransactionManagertransactionManager) {
this.transactionManager = transactionManager;
}
public Object getProxy(Object target) {
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
try{
// 开启事务
transactionManager.beginTransaction();
// 调⽤原有业务逻辑
result = method.invoke(target,args);
// 提交事务
transactionManager.commit();
}catch(Exception e) {
e.printStackTrace();
// 回滚事务
transactionManager.rollback();
// 异常向上抛出,便于servlet中捕获
throw e.getCause();
}
return result;
}
});
}
}
-
修改 beans.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!--跟标签beans,⾥⾯配置⼀个⼜⼀个的bean⼦标签,每⼀个bean⼦标签都代表⼀个类的配置--
>
<beans>
<!--id标识对象,class是类的全限定类名-->
<bean id="accountDao"class="com.wsy.test.dao.impl.JdbcAccountDaoImpl">
<property name="ConnectionUtils" ref="connectionUtils"/>
</bean>
<bean id="transferService"class="com.wsy.test.service.impl.TransferServiceImpl">
<!--set+ name 之后锁定到传值的set⽅法了,通过反射技术可以调⽤该⽅法传⼊对应的值-->
<property name="AccountDao" ref="accountDao"></property>
</bean>
<!--配置新增的三个Bean-->
<bean id="connectionUtils"class="com.wsy.test.utils.ConnectionUtils"></bean>
<!--事务管理器-->
<bean id="transactionManager"class="com.wsy.test.utils.TransactionManager">
<property name="ConnectionUtils" ref="connectionUtils"/>
</bean>
<!--代理对象⼯⼚-->
<bean id="proxyFactory" class="com.wsy.test.factory.ProxyFactory">
<property name="TransactionManager" ref="transactionManager"/>
</bean>
</beans>
-
修改 JdbcAccountDaoImpl
public class JdbcAccountDaoImpl implements AccountDao {
private ConnectionUtils connectionUtils;
public void setConnectionUtils(ConnectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}
@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;
}
}
-
修改 TransferServlet
@WebServlet(name="transferServlet",urlPatterns = "/transferServlet")
public class TransferServlet extends HttpServlet {
// 1. 实例化service层对象
//private TransferService transferService = new TransferServiceImpl();
//private TransferService transferService = (TransferService)BeanFactory.getBean("transferService");
// 从⼯⼚获取委托对象(委托对象是增强了事务控制的功能)
// ⾸先从BeanFactory获取到proxyFactory代理⼯⼚的实例化对象
private ProxyFactory proxyFactory = (ProxyFactory)BeanFactory.getBean("proxyFactory");
private TransferService transferService = (TransferService)proxyFactory.getJdkProxy(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));
}
}
纯xml模式 (实际企业开发,纯XML模式几乎已经没有了,学习的目的是为了和下面注解开发相对应,便于理解及巩固)
实例化Bean的三种⽅式
-
⽅式⼀:使⽤⽆参构造函数 在默认情况下,它会通过反射调⽤⽆参构造函数来创建对象。如果类中没有⽆参构造函数,将创建 失败。
<!--配置service对象-->
<bean id="userService" class="com.yun.service.impl.TransferServiceImpl">
</bean
-
⽅式⼆:使⽤静态⽅法创建 在实际开发中,我们使⽤的对象有些时候并不是直接通过构造函数就可以创建出来的,它可能在创 建的过程 中会做很多额外的操作。此时会提供⼀个创建对象的⽅法,恰好这个⽅法是static修饰的 ⽅法,即是此种情 况。 例如,我们在做Jdbc操作时,会⽤到java.sql.Connection接⼝的实现类,如果是mysql数据库,那 么⽤的就 是JDBC4Connection,但是我们不会去写 JDBC4Connection connection = new JDBC4Connection() ,因 为我们要注册驱动,还要提供URL和凭证信息, ⽤ DriverManager.getConnection ⽅法来获取连接。 contextConfigLocation com.lagou.edu.SpringConfig org.springframework.web.context.ContextLoaderListener 那么在实际开发中,尤其早期的项⽬没有使⽤Spring框架来管理对象的创建,但是在设计时使⽤了 ⼯⼚模式 解耦,那么当接⼊spring之后,⼯⼚类创建对象就具有和上述例⼦相同特征,即可采⽤ 此种⽅式配置。
<!--使⽤静态⽅法创建对象的配置⽅式-->
<bean id="userService" class="com.yun.factory.BeanFactory"
factory-method="getTransferService"></bean>
-
⽅式三:使⽤实例化⽅法创建 此种⽅式和上⾯静态⽅法创建其实类似,区别是⽤于获取对象的⽅法不再是static修饰的了,⽽是 类中的⼀ 个普通⽅法。此种⽅式⽐静态⽅法创建的使⽤⼏率要⾼⼀些。 在早期开发的项⽬中,⼯⼚类中的⽅法有可能是静态的,也有可能是⾮静态⽅法,当是⾮静态⽅法 时,即可 采⽤下⾯的配置⽅式: Bean的X及⽣命周期 作⽤范围的改变 在spring框架管理Bean对象的创建时,Bean对象默认都是单例的,但是它⽀持配置的⽅式
<!--使⽤实例⽅法创建对象的配置⽅式-->
<bean id="beanFactory"
class="com.yun.factory.instancemethod.BeanFactory"></bean>
<bean id="transferService" factory-bean="beanFactory" factory•method="getTransferService"></bean>
-
Bean的X及⽣命周期
-
作⽤范围的改变 在spring框架管理Bean对象的创建时,Bean对象默认都是单例的,但是它⽀持配置的⽅式改 变作⽤范围。作⽤范围官⽅提供的说明如下图:
-
在上图中提供的这些选项中,我们实际开发中⽤到最多的作⽤范围就是singleton(单例模式)和 prototype(原型模式,也叫多例模式)。配置⽅式参考下⾯的代码:
<!--配置service对象--> <bean id="transferService" class="com.yun.service.impl.TransferServiceImpl" scope="singleton"> </bean>
-
不同作⽤范围的⽣命周期
-
单例模式:singleton 对象出⽣:当创建容器时,对象就被创建了。 对象活着:只要容器在,对象⼀直活着。 对象死亡:当销毁容器时,对象就被销毁了。 ⼀句话总结:单例模式的bean对象⽣命周期与容器相同。
-
多例模式:prototype 对象出⽣:当使⽤对象时,创建新的对象实例。 对象活着:只要对象在使⽤中,就⼀直活着。 对象死亡:当对象⻓时间不⽤时,被java的垃圾回收器回收了。 ⼀句话总结:多例模式的bean对象,spring框架只负责创建,不负责销毁
-
-
Bean标签属性 在基于xml的IoC配置中,bean标签是最基础的标签。它表示了IoC容器中的⼀个对象。换句话 说,如果⼀个对象想让spring管理,在XML的配置中都需要使⽤此标签配置,Bean标签的属性如 下:
-
id属性: ⽤于给bean提供⼀个唯⼀标识。在⼀个标签内部,标识必须唯⼀。
-
class属性:⽤于指定创建Bean对象的全限定类名。
-
name属性:⽤于给bean提供⼀个或多个名称。多个名称⽤空格分隔。
-
factory-bean属性:⽤于指定创建当前bean对象的⼯⼚bean的唯⼀标识。当指定了此属性之后, class属性失效。
-
factory-method属性:⽤于指定创建当前bean对象的⼯⼚⽅法,如配合factory-bean属性使⽤, 则class属性失效。如配合class属性使⽤,则⽅法必须是static的。
-
scope属性:⽤于指定bean对象的作⽤范围。通常情况下就是singleton。当要⽤到多例模式时, 可以配置为prototype。
-
init-method属性:⽤于指定bean对象的初始化⽅法,此⽅法会在bean对象装配后调⽤。必须是 ⼀个⽆参⽅法。
-
destory-method属性:⽤于指定bean对象的销毁⽅法,此⽅法会在bean对象销毁前执⾏。它只 能为scope是singleton时起作⽤
-
-
-
DI 依赖注⼊的xml配置
-
依赖注⼊分类
-
按照注⼊的⽅式分类
-
构造函数注⼊:顾名思义,就是利⽤带参构造函数实现对类成员的数据赋值。
-
set⽅法注⼊:它是通过类成员的set⽅法实现数据的注⼊。(使⽤最多的)
-
按照注⼊的数据类型分类
-
基本类型和String 注⼊的数据类型是基本类型或者是字符串类型的数据。
-
其他Bean类型 注⼊的数据类型是对象类型,称为其他Bean的原因是,这个对象是要求出现在IoC容器 中的。那么针对当前Bean来说,就是其他Bean了。
-
复杂类型(集合类型) 注⼊的数据类型是Aarry,List,Set,Map,Properties中的⼀种类型。
-
-
-
-
-
name:指定注⼊时调⽤的set⽅法名称。(注:不包含set这三个字⺟,druid连接池指定属性名称)
-
value:指定注⼊的数据。它⽀持基本类型和String类型。
-
ref:指定注⼊的数据。它⽀持其他bean类型。写的是其他bean的唯⼀标识。
-
复杂数据类型注⼊ ⾸先,解释⼀下复杂类型数据,它指的是集合类型数据。集合分为两类,⼀类 是List结构(数组结构),⼀类是Map接⼝(键值对) 。
-
接下来就是注⼊的⽅式的选择,只能在构造函数和set⽅法中选择,我们的示例选⽤set⽅法注⼊。
依赖注⼊的配置实现之构造函数注⼊ 顾名思义,就是利⽤构造函数实现对类成员的赋值。它 的使⽤要求是,类中提供的构造函数参数个数必须和配置的参数个数⼀致,且数据类型匹 配。同时需要注意的是,当没有⽆参构造时,则必须提供构造函数参数的注⼊,否则Spring 框架会报错。
在使⽤构造函数注⼊时,涉及的标签是 constructor-arg ,该标签有如下属性:
-
name:⽤于给构造函数中指定名称的参数赋值。
-
index:⽤于给构造函数中指定索引位置的参数赋值。
-
value:⽤于指定基本类型或者String类型的数据。
-
ref:⽤于指定其他Bean类型的数据。写的是其他bean的唯⼀标识。
-
依赖注⼊的配置实现之set⽅法注⼊
-
顾名思义,就是利⽤字段的set⽅法实现赋值的注⼊⽅式。此种⽅式在实际开发中是使⽤最多的注 ⼊⽅式。
在使⽤set⽅法注⼊时,需要使⽤ property 标签,该标签属性如下:
-
-
在List结构的集合数据注⼊时, array , list , set 这三个标签通⽤,另外注值的 value 标签内部 可以直接写值,也可以使⽤ bean 标签配置⼀个对象,或者⽤ ref 标签引⽤⼀个已经配合的bean 的唯⼀标识。
在Map结构的集合数据注⼊时, map 标签使⽤ entry ⼦标签实现数据注⼊, entry 标签可以使 ⽤key和value属性指定存⼊map中的数据。使⽤value-ref属性指定已经配置好的bean的引⽤。 同时 entry 标签中也可以使⽤ ref 标签,但是不能使⽤ bean 标签。⽽ property 标签中不能使 ⽤ ref 或者 bean 标签引⽤对象
-
-
XML+注解相结合模式
-
xml中标签与注解的对应(IoC)
xml形式 | 对应的注解形式 |
---|---|
<bean>标签 | @Component("accountDao"),注解加在类上 bean的id属性内容直接配置在注解后⾯如果不配置,默认定义个这个bean的id为类 的类名⾸字⺟⼩写; 另外,针对分层代码开发提供了@Componenet的三种别名@Controller、 @Service、@Repository分别⽤于控制层类、服务层类、dao层类的bean定义,这 四个注解的⽤法完全⼀样,只是为了更清晰的区分⽽已 |
<bean>标签的 scope属 性 | @Scope("prototype"),默认单例,注解加在类上 |
<bean>标签的 init-method 属性 | @PostConstruct,注解加在⽅法上,该⽅法就是初始化后调⽤的⽅法 |
<bean>标签的 destory-method 属性 | @PreDestory,注解加在⽅法上,该⽅法就是销毁前调⽤的⽅法 |
-
DI 依赖注⼊的注解实现⽅式
@Autowired(推荐使⽤)
@Autowired为Spring提供的注解,需要导⼊包 org.springframework.beans.factory.annotation.Autowired。
@Autowired采取的策略为按照类型注⼊。
public class TransferServiceImpl { @Autowired private AccountDao accountDao; }
如上代码所示,这样装配回去spring容器中找到类型为AccountDao的类,然后将其注⼊进来。这 样会产⽣⼀个问题,当⼀个类型有多个bean值的时候,会造成⽆法选择具体注⼊哪⼀个的情况, 这个时候我们需要配合着@Qualifier使⽤。
@Qualifier告诉Spring具体去装配哪个对象。
public class TransferServiceImpl { @Autowired @Qualifier(name="jdbcAccountDaoImpl") private AccountDao accountDao; }
这个时候我们就可以通过类型和名称定位到我们想注⼊的对象。
@Resource
@Resource 注解由 J2EE 提供,需要导⼊包 javax.annotation.Resource。
@Resource 默认按照 ByName ⾃动注⼊。
public class TransferService { @Resource private AccountDao accountDao; @Resource(name="studentDao") private StudentDao studentDao; @Resource(type="TeacherDao") private TeacherDao teacherDao; @Resource(name="manDao",type="ManDao") private ManDao manDao; }
-
如果同时指定了 name 和 type,则从Spring上下⽂中找到唯⼀匹配的bean进⾏装配,找不 到则抛出异常。
-
如果指定了 name,则从上下⽂中查找名称(id)匹配的bean进⾏装配,找不到则抛出异 常。
-
如果指定了 type,则从上下⽂中找到类似匹配的唯⼀bean进⾏装配,找不到或是找到多个, 都会抛出异常。
-
如果既没有指定name,⼜没有指定type,则⾃动按照byName⽅式进⾏装配;
注意: @Resource 在 Jdk 11中已经移除,如果要使⽤,需要单独引⼊jar包
-
纯注解模式
改造xm+注解模式,将xml中遗留的内容全部以注解的形式迁移出去,最终删除xml,从Java配置类启动
对应注解
@Configuration 注解,表名当前类是⼀个配置类
@ComponentScan 注解,替代 context:component-scan
@PropertySource,引⼊外部属性配置⽂件
@Import 引⼊其他配置类
@Value 对变量赋值,可以直接赋值,也可以使⽤ ${} 读取资源配置⽂件中的信息
@Bean 将⽅法返回对象加⼊ SpringIOC 容器
// @Configuration 注解表明当前类是一个配置类
@Configuration
@ComponentScan({"com.yun.test"})
@PropertySource({"classpath:jdbc.properties"})
public class SpringConfig {
@Value("${jdbc.driver}")
private String driverClassName;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean("dataSource")
public DataSource createDataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(driverClassName);
druidDataSource.setUrl(url);
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
return druidDataSource;
}
}
标签:String,回顾,对象,Spring,private,bean,new,public 来源: https://www.cnblogs.com/wangshaoyun/p/16643508.html