1、spring是什么?
spring是一个轻量的java开源框架,它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。其核心功能是控制反转(ioc)和面向切面编程(aop)。
2、spring的优点
- 非侵入式设计
- 方便解耦,简化开发
- 支持AOP
- 支持声明式事务处理
- 方便程序测试
- 方便集成各种优秀框架
- 降低java ee api的使用难度
3、spring框架的结构体系
4、spring的下载,包含两部分spring框架包和第三方依赖包
spring下载地址,下载后直接解压
4个核心模块
- spring-core:包含spring的核心工具类,spring其它模块都要用到这个包的类
- spring-beans:所有应用都要用到的JAR包,包含访问配置文件,创建和管理beans以及进行控制反转和依赖注入的类
- spring-context:提供了在基础IoC功能上的拓展服务,还提供了许多企业级服务的支持
- spring-expression:定义了spring的表达式语言
第三方依赖:commons.logging的jar包
IOC环境搭建
程序的耦合:调用者与被调用者的依赖关系
我们应该尽量做到编译时不依赖,运行时才依赖,ioc就是用来进行解耦的,而且它的作用只是用来解耦。
需要的包:4+1,4个核心包 + commons.logging
配置文件:
- 位置:任意,一般放在类路径,即src下面
- 名称:任意,个人习惯(bean.xml)
- 约束:spring-framework-4.3.13.RELEASE/docs/spring-framework-reference/html/xsd-configuration.html
<?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 -->
<bean id="userServiceId" class="com.lcz.service.impl.UserServiceImpl"></bean>
</beans>
使用
package com.lcz.controller;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.lcz.service.UserService;
public class UserController {
public static void main(String[] args) {
String xmlPath = "bean.xml";
//获取容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
//通过bean的id获取实例对象
UserService us = (UserService) applicationContext.getBean("userServiceId");
us.addUser();
}
}
bean的两种创建规则:
- BeanFactory:提供的是一种延迟创建bean对象的思想方式,什么时候用什么时候创建
- ApplicationContext:提供的是一种立即创建bean对象的思想方式,xml文件解析完立即创建
bean的三种创建方式
- 调用默认无参构造器:默认情况下,如果类中没有默认无参构造器,则创建失败,抛出异常
- 使用静态工厂方式:使用静态工厂中的方法创建对象,需要使用bean标签中的factory-method属性指定静态工厂中创建对象的方法
<!-- 配置静态工厂方法 -->
<bean id="staticFactory" class="com.lcz.service.Factory" factory-method="getUserServiceImpl"></bean>
- 使用实例工厂中方式:
bean的作用范围:可以通过配置的方式来调整作用范围
配置的属性:bean标签的scope属性
属性的取值:
- singleton:单例的(默认值)
- prototype:多例的(当我们让spring接管struts2的action时,必须配置此值)
- request:作用范围是一次请求和当前请求的转发
- session:作用范围是一次会话
- globalsession:作用范围是一次全局会话(服务器集群时,全局session)
bean的生命周期
两个个属性:init-method,destroy-method,分别在对象创建和销毁时被调用的方法
<bean id="userServiceId" init-method="init" destroy-method="destroy" class="com.lcz.service.impl.UserServiceImpl"></bean>
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("添加成功");
}
public void init() {
System.out.println("对象创建");
}
public void destroy() {
System.out.println("对象被销毁");
}
}
1、单例模式
- 创建:容器已创建,对象就被创建
- 存在:容器存在,对象就存在
- 销毁:容器销毁,对象就被销毁
2、多例模式
- 创建:每次使用时都创建一个对象
- 存在:对象在使用中则一直存在
- 销毁:当对象长时间没使用,并且没别别的对象引用,则由java的垃圾回收器销毁
DI依赖注入
注入的方式,三种:
- 构造函数注入
- set方法注入
- 注解注入
注入的数据类型
- 基本类型和String类型
- bean类型(必须是在spring配置文件中出现过的bean)
- 其它复杂类型(集合类型)
构造函数注入
标签:constructor-arg
标签的属性:上面三个是指定给哪个参数赋值,下面两个是指定参数赋什么值
- type:指定参数的类型
- index:指定参数的索引位置,0开始
- name:指定参数的名称(一般使用这个)
- value:指定基本数据类型或者string数据类型的值
- ref:指定其它bean类型的数据
标签的位置:写在bean标签的内部
<!-- 配置bean -->
<!-- 使用构造函数配置属性 -->
<bean id="userServiceId" class="com.lcz.service.impl.UserServiceImpl">
<constructor-arg name="name" value="张三"></constructor-arg>
<constructor-arg name="age" value="25"></constructor-arg>
<constructor-arg name="today" ref="date"></constructor-arg>
</bean>
<!-- today属性需要用到 -->
<bean id="date" class="java.util.Date"></bean>
public class UserServiceImpl implements UserService {
private String name;
private Integer age;
private Date today;
public UserServiceImpl(String name,int age,Date today) {
this.name = name;
this.age = age;
this.today = today;
// TODO Auto-generated constructor stub
}
@Override
public void addUser() {
System.out.println("添加成功");
}
public void print() {
System.out.println(this.name+this.age+this.today);
}
}
setter方法注入
标签:property
属性:同构造函数注入一样
位置:bean标签内部
<bean id="userServiceId" class="com.lcz.service.impl.UserServiceImpl">
<property name="name" value="李四"></property>
<property name="age" value="27"></property>
<property name="today" ref="date"></property>
</bean>
<!-- today属性需要用到 -->
<bean id="date" class="java.util.Date"></bean>
public class UserServiceImpl implements UserService {
private String name;
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public void setToday(Date today) {
this.today = today;
}
private Integer age;
private Date today;
@Override
public void addUser() {
System.out.println("添加成功");
}
public void print() {
System.out.println(this.name+this.age+this.today);
}
}
复杂类型注入
结构相同,标签可以互换。array,list,set可以互换map,props可以互换
<!-- 复杂类型注入配置 -->
<bean id="userServiceId" class="com.lcz.service.impl.UserServiceImpl">
<property name="strings" >
<array>
<value>asd</value>
<value>qwe</value>
<value>asd</value>
</array>
</property>
<property name="list">
<list>
<value>asd</value>
<value>qwe</value>
<value>asd</value>
</list>
</property>
<property name="set">
<set>
<value>asd</value>
<value>qwe</value>
<value>asd</value>
</set>
</property>
<property name="map">
<map>
<entry key="a" value="AAA"></entry>
<entry key="b" value="BBB"></entry>
</map>
</property>
<property name="prop">
<props>
<prop key="A">AAA</prop>
<prop key="B">BBB</prop>
</props>
</property>
</bean>
基于注解的IOC
需要用的包:4 + 1 + spring-aop包
约束:
<?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">
<!-- 告知spring在创建容器是要扫描的包,当配置了此标签后,spring在创建容器后就回去指定的包及子包下去找对应的注解
标签是在一个context的名称空间里,所以必须先导入context名称空间
-->
<context:component-scan base-package="com.lcz"></context:component-scan>
</beans>
注解分类(复杂类型不能注入List Set Map等)
1、用户创建bean对象
@Component
作用:相当配置了一个bean标签
位置:出现在类上
属性:value。含义是指定bean的id,当不写时默认为当前类的短命,首字母改小写
由此注解衍生的三个注解,与@Component的使用和属性一模一样
① @Controller 一般用于controller层
② @Service 一般用于service层
③ @Repository 一般用于持久层
2、用于注入数据的
@Autowired
作用:自动按照类型注入,只要有唯一的类型匹配就能注入成功,使用注解注入set方法不是必须的。所谓的唯一类型,举个例子,当dao层有一个实体类继承了UserDao接口,我们在service中使用注解,来使用UserDao属性,spring默认找它的实现类,如果唯一,则注入成功,如果不唯一,则有多个类实现UserDao接口,否则将变量名作为bean的id来查找。
@Qualifier
作用:在自动按照类型注入的基础上,再按照bean的id注入
属性:value,用于指定bean的id
注意:在给类成员注入数据时,不能独立使用,必须配合@Autowired,在给方法的形参注入数据时可以独立使用。
@Resource
作用:按照bean的id注入
属性:name,用于指定bean的id
示例:@Resource(name="userDao")
@Value
作用:用于注入基本类型和String类型的数据
属性:value,用于指定基本类型和String类型的值,还可以借助spring中的el表达式读取properties配置文件中的数据
示例:@Value("23")
3、用于改变作用范围的
@Scope
作用:用于改变bean的作用范围
属性:value,取值与xml配置里的scope属性一样
4、和生命周期相关的
@PostConstruct:用于绑定初始化方法
@PreDestroy:用于绑定销毁方法
5、其它注解
@Configuration
作用:将一个类作为配置类
@ComponentScan
作用:用于spring配置扫描包,可以完全脱离xml文件
位置:在类上
@Bean
作用:把方法的返回值注入spring容器
属性:name,用于指定bean的id,如果不指定,默认为方法名称
@Import
作用:导入其它配置类,使用@Import导入的配置类的实例不会进入spring容器
@PropertySource
作用:配置properties文件
使用配置类的案例
//主配置类
package config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
@ComponentScan({"com.lcz"})
@Import({Other.class})
@PropertySource("classpath:config/test.properties")
public class SpringConfiguration {
//配置资源占位符解析器4.3.5及以上不用配
@Bean
public static PropertySourcesPlaceholderConfigurer pspc() {
return new PropertySourcesPlaceholderConfigurer();
}
}
//其它配置类
package config;
import java.util.Date;
import org.springframework.context.annotation.Bean;
public class Other {
@Bean(name="today")
public Date getToday() {
return new Date();
}
}
//使用
package com.lcz.controller;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.lcz.service.impl.UserServiceImpl;
import config.SpringConfiguration;
public class UserController {
@SuppressWarnings("resource")
public static void main(String[] args) {
//获取容器
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);
//通过bean的id获取实例对象
UserServiceImpl us = (UserServiceImpl) applicationContext.getBean("userService");
us.addUser();
}
}
package com.lcz.service.impl;
import java.util.Date;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import com.lcz.dao.UserDao;
import com.lcz.service.UserService;
@Service("userService")
@Scope("singleton")
public class UserServiceImpl implements UserService {
@Resource(name="userDao")
private UserDao ud = null;
//使用properties里的配置数据,使用el表达式
@Value("${username}")
private String name;
@Value("23")
private int age = 0;
@Resource(name="today")
private Date date;
@Override
public void addUser() {
ud.addUser();
System.out.println(name+age+date);
System.out.println("添加客户service");
}
@PostConstruct
public void init() {
System.out.println("init....");
}
}
@Qualifier的形参注入
package config;
import java.util.Date;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
public class Other {
@Bean(name="today")
public Date getToday(@Qualifier("date2")Date d) {
return d;
}
@Bean(name="date1")
public Date date1() {
return new Date();
}
@Bean(name="date2")
public Date date2() {
return new Date();
}
}
AOP:面向切面编程
意义:把我们程序中重复的代码抽取出来,在需要执行的时候,使用动态代理技术,在不修改源码的基础上,对我们的方法进行增强。
优势:减少重复代码、提高开发效率,方便维护
相关概念:
- 连接点(Joinpoint):指那些被拦截到的点,在spring中,这些点是指方法,因为spring只支持方法类型的连接点。
- 切入点(Pointcut):切入点是指我们要对哪些Joinpoint拦截的定义。
- 通知/增强(Advice):通知是指拦截到Joinpoint后要做的事情。通知的类型:①前置通知②后置通知③异常通知④环绕哦通知⑤最终通知。
- 引介(Introduction):引介是一种特殊的通知,在不修改类代码的前提下,引介可以在运行期为类动态的增加一些方法或属性。
- 目标对象(Target):代理的目标对象。
- 织入(Weaving):是指把增强应用到目标对象来创建新的代理对象的过程
- 代理(Proxy):一个类被AOP织入增强后,就产生一个代理类
- 切面(Aspect):是切入点和通知的结合
spring配置AOP(xml配置文件配置)
需要用到的包:4 + 1 + 2 + 2
4 + 1 为基础核心包,第一个2为spring-aop和spring-aspect两个包,第二个2为aspectjweaver和aopalliance两个三方依赖包
aspectjweaver下载地址 aoplliance下载地址
注意aspectjweaver最好1.8.2及以上
约束:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="com.lcz.service.impl.UserServiceImpl"></bean>
<!-- 配置AOP -->
<!-- 第一步:把通知类交给spring来管理 -->
<bean id="aopTest" class="com.lcz.controller.AOPTest"></bean>
<!-- 导入aop名称空间,并使用aop:config进行配置 -->
<aop:config>
<!-- 第三步,使用aop:aspect配置切面id属性用于给切面提供唯一标识,ref属性,用于引用应用通知bean的id -->
<aop:aspect id="test" ref="aopTest">
<!-- 第四步,配置通知类型 method属性,指定增强方法的名称 pointcut属性,用于指定切入点表达式-->
<!--
切入点表达式:execution(表达式)
表达式写法:访问修饰符 返回值 包名.包名...类名.方法名(参数列表)
-->
<aop:before method="forward" pointcut="execution(public void com.lcz.service.impl.UserServiceImpl.addUser())" />
</aop:aspect>
</aop:config>
</beans>
切入点表达式:
表达式:访问修饰符 返回值 包名.包名...类名.方法名(参数列表)
全匹配方式:public void com.lcz.service.impl.UserServiceImpl.addUser()
全通配方式:* *..*.*(..)
注意事项:
- 访问修饰符可以省略
- 返回值可以写成通配符*,表示任意返回值
- 包名可以使用通配符*,表示任意包,但是有几个包得写几个通配符,例如:*.*.*.*表示四个包
- 包名可以使用..表示当前包及其子包,例如com..表示com包和其下面的子包
- 类名和方法名都可以使用通配符*
- 参数列表,可以使用具体类型。基本类型直接写类型(例如 int),引用类型必须写包名加类型(java.lang.Integer)
- 参数类型可以使用通配符*,表示任意类型参数,但是必须有参数
- 参数列表可以使用..表示有无参数均可,有参数,可以是任意类型
实际开发中,一般都是对业务层方法进行增强,* com.lcz.service.impl.*.*(..)
定义通用的切入点表达式:如果是写在了aop:aspect标签内部,则表示只有当前切面可用
<aop:aspect id="test" ref="aopTest">
<!-- 第四步,配置通知类型 method属性,指定增强方法的名称 pointcut属性,用于指定切入点表达式-->
<!--
切入点表达式:execution(表达式)
表达式写法:访问修饰符 返回值 包名.包名...类名.方法名(参数列表)
-->
<aop:before method="forward" pointcut-ref="p1" />
<aop:after method="back" pointcut-ref="p1"/>
<aop:pointcut expression="execution(* com.lcz.service.impl.UserServiceImpl.*(..))" id="p1"/>
</aop:aspect>
如果切入点表达式写在了外面,即aop:config里,则所有切入面都可以使用,但是aop:pointcut必须写在aop:aspect前面
常用通知类型
1、aop:before,前置通知,永远在切入点方法执行之前执行
2、aop:after-returning,后置通知,切入点方法正常执行后执行
3、aop:after-throwing,后置通知,切入点方法异常时执行
4、aop:after,最终通知,切入点执行后,无论是否异常都会执行
5、aop:around,环绕通知
由动态代理可知,环绕通知指的是invoke方法,并且里面有明确的切入点方法调用。spring为我们提供了一个接口:ProceedingJoinPoint,该接口可以作为环绕通知的方法参数来调用。该接口中有一个方法proceed(),它的作用就等于method.invoke(),就是明确调用业务层方法(切入点方法)
public Object around(ProceedingJoinPoint pjp) {
Object ret = null;
try {
ret = pjp.proceed();
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
}
return ret;
}
环绕通知是spring给我们提供的手动指定通知位置的方法
使用注解配置AOP
约束:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.lcz"></context:component-scan>
<!-- 开启spring对注解aop的支持 -->
<aop:aspectj-autoproxy />
</beans>
注解配置案例
package com.lcz.utils;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component("logger")
@Aspect
public class Logger {
@Pointcut("execution(* com.lcz.service.impl.*.*(..))")
public void pt() {}
@Before("pt()")
public void beforeRun() {
System.out.println("前置通知");
}
}
使用纯注解,配置类配置
package config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
@Configuration
@ComponentScan({"com.lcz"})
@Import({Other.class})
@PropertySource("classpath:config/test.properties")
@EnableAspectJAutoProxy
public class SpringConfiguration {
//配置资源占位符解析器4.3.5及以上不用配
@Bean
public static PropertySourcesPlaceholderConfigurer pspc() {
return new PropertySourcesPlaceholderConfigurer();
}
}