spring入门
Spring是一个开源框架,为简化企业级开发而生。它以IOC(控制反转)和AOP(面向切面)为思想内核,提供了控制层SpringMVC、数据层SpringData、服务层事务管理等众多技术,并可以整合众多第三方框架。所以今天就开始学习spring,开始spring入门。
这是spring的体系结构,这些模块可以满足很多企业级开发。其中的core Container是spring的核心模块。
IOC(控制反转)
控制反转:程序将创建对象的权利交给框架。在之前我们创建对象时通过new关键字手动创建的,也就是调用者管理对象。
通过手动创建的形式有两个缺点:
1.浪费资源:如果在方法中要创建对象,那么我们每次调用一次这个方法就要创建一个这个对象,如此一来就会创建大量对象,浪费资源。
2.代码的耦合度高:举个栗子,我们创建一个接口,也创建了一个实现类,随着开发,我又创建了一个更好的实现类,那我就必须修改源码。
通过spring实现IOC
在spring中有一个容器来管理对象。我们使用maven来演示spring的用法。
在maven中我们要引入的依赖如下:
spring的依赖:org.springframework spring-context
测试依赖:junit junit
编写applicationcontext.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="studentDao"class="com.hk.dao.StudentDaoImpl">
</bean>
</beans>
创建domain类和Dao类
public class Student {
private int id;
private String name;
private String address;
// 省略getter/setter/构造方法/tostring
}
public interface StudentDao {
// 根据id查询学生
Student findById(int id);
}
public class StudentDaoImpl implements
StudentDao{
@Override```
public Student findById(int id) {
// 模拟从数据库查找出学生
return new Student(1,"k","jx");
}
}
//测试类
public class TestStudentDao {
@Test
public void testSelectById(){
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
StudentDao studentDao = (StudentDao) ac.getBean("studentDao");
Student student = studentDao.selectById(1);
System.out.println(student);
}
}
在测试时我们并没有手动创建实现类,而死从容器中把对象取出来用,这就是最简单的控制反转IOC。
spring容器类型
Spring容器中的顶层接口是BeanFactory,ApplicationContext是BeanFactory的子接口。ApplicationContext有以下三个常用实现类:
不同的实现类读取文件位置不同
ClassPathXmlApplicationContext:该类可以从项目中读取配置文件
FileSystemXmlApplicationContext:该类从磁盘中读取配置文件
AnnotationConfigApplicationContext:使用该类不读取配置文件,而是会读取注解
对象创建方式
我们平时都是用new关键字来创建对象,那么spring容器是通过什么方式来创建对象呢?
默认是使用空参构造法来创建对象,那么加入类中没有空参构造,怎么办呢?这时候我们可以通过调用工厂类来创建对象。
创建工厂类,并提供创建对象的方法
这是非静态方法
public class StudentDaoFactory {
public StudentDao getStudentDao(){
return new StudentDaoImpl(1);
}
}
<!-- id:工厂对象的id,class:工厂类 -->
<bean id="studentDaoFactory"
class="com.itbaizhan.dao.StudentDaoFactory
"></bean>
<!-- id:bean对象的id,factory-bean:工厂对象
的id,factory-method:工厂方法 -->
<bean id="studentDao" factory-bean="studentDaoFactory" factory-method="getStudentDao"></bean>
这是静态方法
public class StudentDaoFactory2 {
public static StudentDao getStudentDao2() {
return new StudentDaoImpl();
}
}
<!-- id:bean的id class:工厂全类名 factory-method:工厂静态方法 -->
<bean id="studentDao"class="com.hk.dao.StudentDaoFactory2" factory-method="getStudentDao2"></bean>
在配置文件中的写法是有点区别的。
对象的创建策略和销毁时机
只要在中添加属性scope就可以设置对象的创建策略,那么创建策略是什么,通俗的讲就是容器会创建几个对象,一共有5种创建策略:
singleton:单例,默认策略。整个项目只会创建一个对象,这是默认的,通过 中的 lazy-init 属性可以设置单例对象的创建时机:false是立即创建这是默认的,而true是延迟创建,第一次使用bean对象才会创建。销毁时机:对象随着容器的销毁而销毁。
prototype:多例,每次从容器中获取时都会创建对象。销毁时机:使用JAVA垃圾回收机制销毁对象。
request:每次请求创建一个对象,只在web环境有效。销毁时机:当处理请求结束,bean实例将被销毁。
session:每次会话创建一个对象,只在web环境有效。销毁时机:当HTTP Session最终被废弃的时候,bean也会被销毁掉。
gloabal-session:一次集群环境的会话创建一个对象,只在web
环境有效。销毁时机:集群环境下的session销毁,bean实例也将被销毁。
生命周期方法
bean对象的生命周期包含创建、使用和销毁,在spring中是可以配置bean对象在创建和销毁时自动执行的方法。
定义一个StudentDao的实现类加入init方法和destroy方法,并通过bean配置到容器中,bean中的init属性与destroy属性分别加入这连个方法
public class StudentDaoImpl2 implements StudentDao{
public void init(){
System.out.println("创建StudentDao");
}
public void destroy(){
System.out.println("销毁StudentDao");
}
public Student selectById(Integer id) {
return null;
}
}
<bean id="studentDao" class="com.hk.dao.StudentDaoImpl2" scope="singleton" init-method="init" destroy-method="destroy"></bean>
测试
@Test
public void t1(){
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//销毁容器
ac.close();
}
获取Bean对象的方式
spring中有多种获取对象的方式
1.通过id/name获取:此时需要强制类型转换
2.通过类型获取:不需要强制类型转换
3.通过类型+id/name获取:更加精确
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
StudentDao studentDao = (StudentDao) ac.getBean("studentDao");
StudentDao studentDao1 = ac.getBean(StudentDao.class);
StudentDao studentDao2 = ac.getBean("studentDao", StudentDao.class);
Student student = studentDao.selectById(1);
Student student1 = studentDao1.selectById(1);
Student student2 = studentDao2.selectById(1);
System.out.println(student);
System.out.println(student1);
System.out.println(student2);
依赖注入
什么是依赖注入
依赖注入是控制反转的具体实现,控制反转是将创建对象的权力交给容器,但是对象中可能会依赖其他对象,这时候就需要依赖注入。
public class StudentService {
//不是用注入时,我们需要手动注入属性值
private StudentDao studentDao;
public Student findStudentById(Integer id){
return studentDao.selectById(id);
}
}
这样写的弊端是,比如说,当我的studentService想要想要使用StudentDao的另一个实现类时,需要修改源码,这样耦合度就很高,代码的可维护性降低,使用spring之后,spring管理Service对象与Dao对象,此时它能够为Service对象注入依赖的Dao属性值。
依赖注入的方式
在之前开发,我们想要设置对象属性可以使用setter方法和构造方法。所以依赖注入也可以有两种注入方式
1.setter注入:一定记得在StudentService中添加setter方法
<bean id="studentDao" class="com.huangkai.dao.StudentDaoImpl"></bean>
<bean id="studentService" class="com.hk.service.StudentService">
<!--name:对象的属性名 ref:容器中对象的id值-->
<property name="studentDao" ref="studentDao"></property>
</bean>
2.构造方法注入:同样被注入类(StudentService)编写有参的构造方法
<bean id="studentDao"class="com.hk.dao.StudentDaoImpl">
</bean>
<bean id="studentService" class="com.hk.service.StudentService">
<!-- name:对象的属性名 ref:配置文件中注入对象的id值 -->
<constructor-arg name="studentDao"ref="studentDao"></constructor-arg>
</bean>
3.自动注入
自动注入不需要在Bean中添加其他标签的属性值,而是自动从容器中找到相应的bean对象设置为属性值。因为是自动,所以我们要加入一些配置:
1.全局配置:在 中设置 default-autowire 属性可以定义所有bean对象的自动注入策略。
2.局部配置:在 中设置 autowire 属性可以定义当前bean对象的自动注入策略。
autowire的取值如下:
no:不会进行自动注入。
default:全局配置default相当于no,局部配置default表示使用全局配置
byName:在Spring容器中查找id与属性名相同的bean,并进行注入。需要提供set方法。
byType:在Spring容器中查找类型与属性类型相同的bean,并进行注入。需要提供set方法。
constructor:在Spring容器中查找id与属性名相同的bean,并进行注入。需要提供构造方法。
举个栗子:
<bean id="studentDao" class="com.huangkai.dao.StudentDaoImpl"></bean>
<bean id="studentService" class="com.huangkai.service.StudentService" autowire="byName"></bean>
这样子写就是当StudentService需要使用StudentDao时,StudentService就会直接从容器中匹配有没有id名字与studentService匹配的,一定要注意setter方法和构造方法,不要忘记加上。
依赖注入的类型
依赖注入支持注入bean类型、基本数据类型和字符串、List集合、Set集合、Map集合、Properties对象类型等。
通过注解实现springIOC
注解实现IOC要在配置文件中添加东西所以添加一个新的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
@Component
作用:用于创建对象,放入Spring容器,相当于 bean id=“” class=“”
位置:类上方
我们直接写注解,spring容器是不知道的,想要通过注解加入到容器中我们要在配置类中添加配置
<context:component-scan base-package="com.hk"></context:component-scan>
@Repository、@Service、@Controller
作用:这三个注解和@Component的作用一样,使用它们是为了区分该类属于什么层。
@Repository用于Dao层
@Service用于Service层
@Controller用于Controller层
@Scope
作用:指定bean的创建策略
位置:类上方
取值:singleton prototype request session globalsession
@Autowired
作用:从容器中查找符合属性类型的对象自动注入属性中。用于代替 中的依赖注入配置。
位置:属性上方、setter方法上方、构造方法上方。
写在属性上方进行依赖注入时,可以省略setter方法。
@Qualifier
作用:在按照类型注入对象的基础上,再按照bean的id注入。
位置:属性上方
注意:@Qualifier必须和@Autowired一起使用。
@Value
作用:注入String类型和基本数据类型的属性值。
位置:属性上方
注意注入配置文件的值时,写法是这样@Value(“${jdbc.username}”)
@Configuration
此时基于注解的IOC配置已经完成,但是我们依然离不开Spring的xml配置文件。接下来我们脱离bean.xml,使用纯注解实现IOC。就算使用纯注解,我们还是会保留配置文件。
@ComponentScan
作用:指定spring在初始化容器时扫描的包。
位置:配置类上方
@PropertySource
作用:代替配置文件中的 context:property-placeholder 扫描配置文件
位置:配置类上方
注意:配置文件位置前要加关键字 classpath
@Bean
作用:将方法的返回值对象放入Spring容器中。如果想将第三方类的对象放入容器,可以使用@Bean
位置:配置类的方法上方。
属性:name:给bean对象设置id
注意:@Bean修饰的方法如果有参数,spring会根据参数类型从容器中查找可用对象。
@Import
作用:如果配置过多,会有多个配置类,该注解可以为主配置类导入其他配置类
位置:主配置类上方
AOP(面向切面)
面向切面:在不修改源码的基础上,对已有的方法进行增强,主要作用在事务处理、日志管理、权限控制、异常处理等方面。
aop中有一些术语:
连接点:指能被拦截到的点,在spring中只有方法能被拦截。
切点:指要对哪些拦截点进行拦截,就是要被增强的方法。
通知:拦截后要做的事,就是拦截后执行的方法。
切面:切点+通知
AOP的通知类型
aop有五种通知类型
1.前置通知(before):在方法执行前添加功能
2.后置通知(after-returning):在方法正常执行后添加功能
3.异常通知(after-throwing):在方法抛出异常后添加功能
4.最终通知(after):无论方法是否抛出异常,都会执行该通知
5.环绕通知(around):在方法执行前后添加功能
切点表达式
标准写法:访问修饰符 返回值 包名.类名.方法名(参数列表)
访问修饰符可以省略。
返回值使用 * 代表任意类型。
包名使用 * 表示任意包,多级包结构要写多个 * ,使用 … 表示任意包结构
类名和方法名都可以用 * 实现通配。
参数列表:基本数据类型直接写类型
引用类型写 包名.类名
* 表示匹配一个任意类型参数
… 表示匹配任意类型任意个数参数
全通配: * ….(…)
Aspectj编写AOP
Aspectj是一个基于Java语言的AOP框架,我们使用Aspectj来实现AOP
举一个简单的栗子:在一个新增方法中添加后置通知
先引入依赖
<dependencies>
<!-- spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.13</version>
</dependency>
<!-- AspectJ -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
先编写Dao类
@Repository
public class UserDao {
public void add(){
System.out.println("新增用户");
}
}
编写通知类
public class MyAspectJAdvice {
// 后置通知
public void myAfterReturning() {
System.out.println("打印日志...");
}
}
在配置类中引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.huangkai"></context:component-scan>
<!--通知对象-->
<bean id="myAspectJAdvice" class="com.hk.Aspectj.MyAspectjAdvice"></bean>
<!--配置AOP-->
<aop:config>
<!--配置斜面-->
<aop:aspect ref="myAspectJAdvice">
<!--配置切点-->
<aop:pointcut id="myPointcut" expression="execution(* com.huangkai.Dao.UserDao.*(..))"/>
<aop:after-returning method="myAfterRunning" pointcut-ref="myPointcut"/>
</aop:aspect>
</aop:config>
</beans>
写法都是固定的,只要依葫芦画瓢就行。测试一下
public class UserDaoTest {
@Test
public void testAdd(){
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) ac.getBean("userDao");
userDao.add();
}
}
注解配置AOP
可以使用注解替代配置文件配置切面
先要在xml中开启注解支持
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
在通知类上加上@Aspect,在通知方法上加入@Before/@AfterReturning/@AfterThrowing/@After/@Around即可
public class UserDaoTest {
@Test
public void testAdd(){
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) ac.getBean("userDao");
userDao.add();
}
}
除了Aspecj,当然也可以使用原生Java代码来实现,先掌握Aspectj即可。