一.介绍
一款分层的一站式(full-stack)开源框架, 由持久层,WEB层…组成 , 其中两大核心技术为: IOC控制反转和AOP面向切面编程.
二.基本概念
1.控制反转IOC : 谈到反转 ,那必须要提正转的概念,所谓正转 ,即A类中调用B类的方法,对象的控制权在程序员手里 ,那么显然,反转就是将对象的控制器从程序员转到spring容器中进行管理 , 具体的实现是由DI(Dependency Injction,依赖注入实现)
(白嫖一张图👆)
2.依赖注入 : 在运行期间由外部容器动态地将依赖对象注入容器中,注入方式在不考虑工厂模式的情况下有三种,即 : set方法注入,构造器注入,使用注解注入 , 下面会给出代码
3.AOP面向切面编程 : 这个下面会详细讲,所谓面向切面编程,就是通过预编译和动态代理的方式实现程序功能的统一维护,简单点讲,就是将于业务逻辑无关的部分代码单独划分出去
4.Bean : 在spring中,所有的对象我们都称之为Bean
三.搭建框架
1.拷贝jar包 : 选择与jdk版本匹配的jar包,版本差太大可能无法匹配
2.创建配置文件xml : 推荐的命名为applicationContext.xml
3.拷贝约束,并使用bean标签同一管理类 :
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
"></beans>
4.托管类 : <bean></bean>
标签的id属性是唯一标识,也就是类名(当然在这里类名的首字母是小写的),class属性是类的位置
5.在主方法中加载spring配置文件,从容器中获取bean :
这里我们使用到的classpath,就是前文提到的当tomcat发布后,WEB-INF下的classes的路径
//加载配置文件
AbstractApplicationContext context = //springframework下的包
new ClassPathXmlApplicationContext("applicationContext.xml");
👆参数传递配置文件名
//获取bean
context.getBean(UserService.class);根据类型获取
UserService ud = (UserService)context.getBean("userService");根据名字获取
注 : 由于此时类是由spring管理的,默认bean为单例(也就是产出的bean的地址[哈希码]都是一样的) , 当然我们也可以通过scope属性修改为多例:scope:singleton
标识对象为单例,而scope:prototype
则是多例,并且可配合多线程实现一个线程一个实例
6.使用set方法注入(不唯一),需要用到property
标签,name是属性名,value是属性值
7.对象的生命周期 , 这涉及到了对象的创建与销毁,我们使用getBean()
方法调用初始化对象,使用context.registerShutDownHook()
方法销毁对象,在队里的情况下,对象不会被销毁[只能通过GC机制销毁多例对象]
(我们也可以通过实现接口的方式进行创建销毁Bean)
代码直接在下面给出吧
四.三种注入方式
我们在前面用到了set方法注入,那么下面先给出set方法注入的代码
1.基于set()方法的注入
public static void main(String[] args) {
AbstractApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
UserService ud = (UserService)context.getBean("userService");
System.out.println(ud);
System.out.println(ud.getUsername());
context.registerShutdownHook(); //拆卸Bean
}
applicationContext.xml
<bean id="userService" class="com.hzyc.lesson.service.UserService"
init-method="myInit" destroy-method="myDestroy">
<property name="username" value="张三" >
<!-- set()方法注入 -->
</property>
</bean>
生命周期
public void myInit() {
System.out.println("生命周期开始");
}
public void myDestroy() {
System.out.println("生命周期结束");
}
2.基于构造器的注入
<bean id="goodService" class="com.hzyc.lesson.service.GoodService" >
<property name="goodName" value="PPP"></property>
</bean>
<bean id="userService"
class="com.hzyc.lesson.service.UserService">
<constructor-arg index="0" value="zs"></constructor-arg>
<constructor-arg index="1" ref="goodService">
</constructor-arg>
</bean>
注意参数与index属性的匹配
public UserService(String username, GoodService gs) {
this.username = username;
this.gs = gs;
}
3.注入集合
[普通数据用value,引用数据用ref]
list (线性)
<bean id="userService" class="com.hzyc.lesson.service.UserService" >
<property name="myList">
<list>
<value>长春市</value>
<value>吉林市</value>
</list>
</property>
</bean>
set (线性)
<property name="mySet">
<set>
<value>长春市</value>
<value>吉林市</value>
</set>
</property>
map (键值匹配)
<property name="myMap">
<map>
<entry key="1" value="长春"/>
<entry key="2" value="吉林"/>
</map>
</property>
property (键值匹配,跟map使用方法一样)
<property name="myProp">
<map>
<entry key="1" value="长春"/>
<entry key="2" value="吉林"/>
</map>
</property>
4.基于注解的注入
当项目中bean的数量庞大时,我们不可能一个一个去注入,而是使用标签组件扫描<context:component-scan base-package="要扫描的包"></~>
,然后再打上注解
注解 : 注解一般打在类上,使用注解后相当于在xml配置文件中使用bean注册, 类是控制器 : @controller
类是service(业务) @Service
dao层 @Responsity
无法确定类是哪一层时使用 @component
注解的作用是相同的,但是建议对应层打上对应注解提高可读性
<context:component-scan
base-package="com.hzyc.lesson"></context:component-scan>
在一个类中注入另一个类时,这两个类都要使用 @Autowired
注解,在引用的类上打上注解@Autowired
使用@Autowired
注解时,如果存在接口 ,那么要把注解打在接口的实现类中
注意如果同时存在两个以上的实现类,那么系统会报错.
这个时候可以选择使用@Resource
去注册或者在前者基础上增加一个@Qualifer
并且使用别名去注册
代码👇
@Autowired
@Qualifier(value="gd1") //别名在接口的实现类中通过注解定义
private GoodDao gd;
@Resource(name="gd1") 写name是按名字(别名)注入
private GoodDao gd;
@Resource(type=GoodDaoImpl2.class) 按类型注入
private GoodDao gd;
5.注意事项 : 当使用set方法注入的时候,需要被注入的那个类中,构造函数和set()方法不能同时存在,否则会Error creating bean with name 'userService' defined in class path resource
五.生命周期的接口实现
实现InitializingBean,DisposableBean接口
public class UserService implements InitializingBean,DisposableBean{
private String username;
public void add() {
System.out.println("添加...");
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public void destroy() throws Exception {
// TODO Auto-generated method stub
System.out.println("通过接口销毁");
}
@Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
System.out.println("通过接口生成");
}
}
去掉init和destroy属性
<bean id="userService" class="com.hzyc.lesson.service.UserService" >
<property name="username" value="张三" >
</property>
</bean>