文章目录
1. Spring
Spring的核心要素有两点,
ioc(Inversion of Control)
和aop(Aspect Oriented Programmin)
,即控制反转和面向切面编程
1.1 控制反转(IOC)
Spring框架能够提供对程序中对象的创建、赋值和管理,而不需要我们手动去创建对象,IOC的底层采用的是反射机制,Servlet就是一种IOC的体现,在Servlet程序中,我们并没有创建过Servlet对象,而全部都是由Tomcat服务器提供对Servlet的创建、管理和销毁等操作,我们只需要专注于业务逻辑的实现,简化开发流程。
开发流程
- 配置pom.xml文件,加入spring框架各个组件的坐标
- 编写spring配置文件
applicationContext.xml
,放置于resources
目录中- 在spring配置文件中加入各个组件
bean
- 实现文件中可以对直接对bean进行调用
示例
spring.xml配置文件有固定的格式:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--在spring配置文件中配置所需要的bean-->
<!--
id是所需要的类对象的唯一标识
class代表id所对应的类的全限定类名
-->
<bean id="userDao" class="com.xxx.xxx.UserDaoImpl"></bean>
这样配置之后就可以使用该类的实例对象,使用方法为:
public static void main(String[] args){
// 通过spring的核心配置文件来加载其中定义的bean
ApplicationContext app = new ClassPathXmlApplicationContext('applicationContext');
// 以id为参数来获取对应的类对象
UserDao userDao = (UserDao)app.getBean('userDao');
// 可以调用userDao的方法
//...
}
在Spring中,框架采用反射机制,默认使用bean的无参构造方法来创建实例,在配置bean时,可以指定bean的作用范围,通常有singleton
和prototype
两种方式,方式的写法如下:
<bean id="userDao" class="com.xxx.xxx.UserDaoImpl" scope="singleton"></bean>
这两种方法的区别如下表,Spring默认创建是singleton
参数 | 作用 | 作用范围 |
---|---|---|
singleton | 框架中只包含一个实例 | 在读取主配置文件时就创建对象 |
prototype | 框架中可以包含多个实例 | 调用一次getBean方法就创建一个新对象 |
在创建Bean实例时,可以指定方法来指定bean进行初始化init
或者销毁时的动作
<bean id="userDao" class="com.xxx.xxx.UserDaoImpl" init-method="init" destory-method="destory"></bean>
同时需要在userDao
的类中添加对应名称的方法
public class UserDaoImpl {
public void init(){
// do some...
}
public void destory(){
// do some...
}
}
最后,Bean的实例化方式除了无参构造方法以外,还有两种方法
- 工厂静态方法实例化
- 工厂实例方法实例化
在这两种方法中,需要实现bean的工厂类,并在工厂中定义实例创建的方法,同时需要在Spring的配置文件applicationContext.xml
文件中添加工厂的bean,并在其中添加属性factory-method
,采用工厂中的方法来构造实例
如果是静态工厂方法,那么只需要实现一个工厂类,调用其中的静态方法;但如果是工厂实例方法,那么需要先生成一个工厂的bean,再在其中调用工厂中的方法。
1.2 依赖注入(DI)
1.2.1 引用类型依赖注入
想在某个类,比如UserService
类中注入UserDao
类的对象(UserService
类中有UserDao
类的实例),有以下几种方法(在框架的层面实现注入而不需要进入到代码中):
1)set方法注入
<bean id="userDao" class="com.xxx.xxx.UserDaoImpl"></bean>
<bean id="userService" class="com.xxx.xxx.UserServiceImpl">
<!--
第一个name是UserService类中setUserDao()方法的set后面的名称
第二个ref属性代表这个引用了上方id为userDao的bean来进行注入
-->
<property name="userDao" ref="userDao"></property>
</bean>
2)有参构造器注入
这个方法需要在UserService
类中提供一个有参构造方法
public class UserService{
private UserDao userDao;
// constructor
public UserService(UserDao userDao){
this.userDao = userDao;
}
// other method...
}
然后配置主配置文件
<bean id="userDao" class="com.xxx.xxx.UserDaoImpl"></bean>
<bean id="userService" class="com.xxx.xxx.UserServiceImpl">
<!--
第一个name是UserService类中构造方法的参数名
第二个ref属性代表这个引用了上方id为userDao的bean来进行注入
-->
<constructor-arg name="userDao" ref="userDao"></constructor-arg>
</bean>
1.2.2 普通数据类型依赖注入
public class UserService{
private String userName;
private int age;
// getter and setter method...
}
<bean id="userService" class="com.xxx.xxx.UserServiceImpl">
<property name="userName" value="zhangsan"></property>
<property name="age" value="18"></property>
</bean>
1.2.3 集合类型依赖注入
public class UserService{
private List<String> strLists;
private Map<String, User> userMap;
// getter and setter method...
}
public class User{
private String userName;
private int age;
// getter and setter method...
}
<!--user对象,用于依赖注入到map的value中-->
<bean id="userOne" class="com.xxx.xxx.User">
<property name="userName" value="zhangsan"></property>
<property name="age" value="18"></property>
</bean>
<bean id="userTwo" class="com.xxx.xxx.User">
<property name="userName" value="lisi"></property>
<property name="age" value="28"></property>
</bean>
<bean id="userService" class="com.xxx.xxx.UserServiceImpl">
<property name="strLists">
<list>
<value>aaa</value>
<value>bbb</value>
<value>ccc</value>
</list>
</property>
<property name="userMap">
<map>
<entry key="userone" value-ref="userOne"></entry>
<entry key="usertwo" value-ref="userTwo"></entry>
</map>
</property>
</bean>
1.2.4 配置数据库连接池
采用alibaba的Druid数据库连接池,在spring的主配置文件中配置如下,同时还需要在pom.xml
文件中引入各个依赖项的坐标
<!--加载外部的properties文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--数据库连接池配置 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close" lazy-init="false">
<!--以下是各项配置参数的设置-->
<!--属性可以在jdbc.properties配置文件中读取-->
<!--property后面的name是setter方法的set后面的那个属性名-->
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="initialSize" value="1" />
<property name="maxActive" value="50" />
<property name="maxWait" value="30000" />
<property name="filters" value="stat,wall" />
<property name="timeBetweenEvictionRunsMillis" value="3000" />
<property name="minEvictableIdleTimeMillis" value="300000" />
<property name="validationQuery" value="SELECT 'x'" />
</bean>
1.3 注解(Annotation)
使用注解可以大大减少配置xml文件的工作,简化开发流程
1.3.1 原始注解
注解 | 说明 |
---|---|
@Component | 在类上标注用于实例化Bean |
@Controller | 使用在web项目中的controller层类上实例化Bean |
@Service | 使用在web项目中的service层类上实例化Bean |
@Repository | 使用在web项目中的dao层类上实例化Bean |
@Autowired | 使用在字段上用于类型注入(Spring会根据字段类型去寻找对应的bean注入) |
@Qulifier | 配合@Autowired 一起使用,使得@Autowired 可以根据名称进行注入,一般写为@Qulifier("xxx") |
@Resource | 相当于@Qulifier +@Autowired ,依照名称进行注入 |
@Value | 对普通类型注入属性,通常用于配置文件的读取注入参数@Value(${xxx}) |
@Scope | 标注Bean的作用范围,一般用在类上,常用@Scope("singleton") 或者@Scope("prototype") |
@PostConstruct | 使用在方法上标注该方法是Bean的初始化方法,通常用于init() 方法 |
@PreDestory | 使用在方法上标注该方法是Bean的销毁方法 |
下面的几个类给出了一些常用的方式
// 业务逻辑层,根据请求实现特定的逻辑
@Service
public class MusicServiceImpl implements MusicService {
@Autowired
private MusicDao musicDao;
@Override
public int addMusic(Music music) {
int result = musicDao.insertMusic(music);
return result;
}
@Override
public List<Music> findMusics() {
return musicDao.selectMusics();
}
}
// web层,处理请求,调用相应的service
@Controller
public class MusicController {
@Resource
private MusicService musicService;
// 添加音乐
@RequestMapping("/addMusic.do")
public ModelAndView addMusic(Music music){
ModelAndView mv = new ModelAndView();
String tips = "注册失败";
// 调用service处理添加音乐
int res = musicService.addMusic(music);
if(res > 0){
tips = "音乐【" + music.getName() + "】添加成功";
}
// 结果页面
mv.addObject("tips", tips);
// 指定结果页面
mv.setViewName("result");
return mv;
}
}
但是使用上面的这些注解,只能够注解我们自定义的一些类,对于一些不是我们自定义的第三方类,就束手无策了,例如alibaba的druid数据库连接池,如果使用上面的注解方式,就必须找到druid连接池的源码并在上面进行注解,这显然不现实,那么为了完全替代xml配置文件,需要使用到一些新的注解。
1.3.2 新注解
注解 | 说明 |
---|---|
@Configuration | 用于指定当前类是一个Spring核心配置类,当创建容器时会从该类上进行注解的加载 |
@ComponentScan | 用于指定Spring在初始化容器时需要扫描的包 |
@Bean | 用在方法上,将方法的返回值存储到Spring容器中 |
@PropertySource | 用于加载.properties配置文件中的信息 |
@Import | 用于导入其他的配置类 |
示例代码
// 核心配置类
// 标志该类是Spring的核心配置类
@Configuration
// 组件扫描器,扫描参数所对应的包
@ComponentScan("com.xxx.xxx")
// 加载数据库连接池的配置
@Import({DataSourceConfiguration.class, xxx.class})
public class SpringConfiguration{
}
// 数据库连接池配置
@PropertySource("classpath:xxx") // 读取配置文件
public class DataSourceConfiguration{
@Value("${jdbc.driver}") // 读取配置文件中的信息,解耦合
private String driverClass;
// 其余配置文件信息...
@Bean("xxx") // 参数为对象在容器中的名称,Spring会将当前方法的返回值以xxx的名称存储在容器中
public DataSource getDataSource(){
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass(driverClass);
// 其余设置项...
return dataSource;
}
}
那么在main函数中的写法变为了
public static void main(String[] args){
ApplicationContext app = new AnnotationConfigurationContext(SpringConfiguration.class);
xxx cls = app.getBean(xxx.class);
cls.method();
}