目录
a. IOC(Inversion of Control):反转控制
IOC 、AOP为内核,轻量级开发框架
反转控制(依赖注入)、面向切面编程
1.spring优势:
-
方便解耦,简化开发
-
AOP编程的支持,面向切面编程(区别于面向对象编程)
把公共的类抽取出来,这个类就叫切面
-
声明式事务的支持
-
方便程序的测试
-
方便集成各种优秀的框架
SpingMVC,Mybatis,Shiro等
-
降低javaEEAPI的使用难度
-
java 源码是经典的学习范例
2. spring 体系架构
3. 入门(传统方式)
-
导入坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--设置字符集,jdk版本-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
-
创建UserDao接口,以及实现类
-
创建spring核心配置文件
<?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="User" class="com.xjl.dao.imp.UserImp"/>
</beans>
-
测试
@Test
public void springTest(){
ApplicationContext app = new ClassPathXmlApplicationContext("spring.xml");
UserDao user = (UserDao) app.getBean("User");
String username = user.getUsername();
System.out.println("username: "+username);
}
属性值的注入
-
1、导入架包
-
maven 会自动导依赖架包,因此只需要导入spring-context包即可
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.9.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> </dependency>
-
-
2、创建类
-
属性需要有set,get方法
-
-
3、创建spring的配置文件,并配置类
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 配置bean id属性:配置bean的名称,该属性值在IOC容器中是唯一的 class属性:配置bean的全类名,Spring会利用反射技术实例化该bean --> <bean id="helloWorld" class="com.atguigu.spring.helloworld.HelloWorld"> <!-- 通过property标签给bean的属性赋值 --> <property name="name" value="Spring"></property> </bean> </beans>
-
测试
//1.创建IOC容器对象 ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml"); //2.从IOC容器中获取HelloWorld对象 HelloWorld helloWorld = (HelloWorld) ioc.getBean("helloWorld"); //3.调用HelloWorld中的sayHello方法 helloWorld.sayHello();
4. Bean标签范围
scope:指对象的作用范围,取值如下
取值范围 | 说明 |
---|---|
singleton | 默认值,单例的 |
prototype | 多例的 |
request | WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中 |
session | WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中 |
global session | WEB 项目中,应用在 Portlet 环境,如果没有 Portlet 环境那么globalSession 相当于 session |
1)当scope的取值为singleton时
Bean的实例化个数:1个
Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例
Bean的生命周期:
对象创建:当应用加载,创建容器时,对象就被创建了
对象运行:只要容器在,对象一直活着
对象销毁:当应用卸载,销毁容器时,对象就被销毁了
2)当scope的取值为prototype时
Bean的实例化个数:多个
Bean的实例化时机:当调用getBean()方法时实例化Bean
对象创建:当使用对象时,创建新的对象实例
对象运行:只要对象在使用中,就一直活着
对象销毁:当对象长时间不用时,被 Java 的垃圾回收器回收了
5. Bean 的三种实例化方式
-
无参构造方法实例化
-
工程静态方法实例化
-
工厂实例方法实例化
6. 依赖注入
a. IOC(Inversion of Control):反转控制
在应用程序中的组件需要获取资源时,传统的方式是组件主动的从容器中获取所需要的资源,在这样的模式下开发人员往往需要知道在具体容器中特定资源的获取方式,增加了学习成本,同时降低了开发效率。
反转控制的思想完全颠覆了应用程序组件获取资源的传统方式:反转了资源的获取方向——改由容器主动的将资源推送给需要的组件,开发人员不需要知道容器是如何创建资源对象的,只需要提供接收资源的方式即可,极大的降低了学习成本,提高了开发的效率。这种行为也称为查找的被动形式。
b. DI依赖注入
IOC的另一种表述方式:即组件以一些预先定义好的方式(例如:setter 方法)接受来自于容器的资源注入。相对于IOC而言,这种表述更直接。
IOC 描述的是一种思想,而DI(Dependency Injection) 是对IOC思想的具体实现.
dao注给service
-
构造方法
-
setter
-
创建HelloWord类
public class HelloWorld {
private String name;
public HelloWorld() {
System.out.println("HelloWorld对象被创建了");
}
public void setName(String name) {
System.out.println("name属性被赋值了");
this.name = name;
}
public void sayHello() {
System.out.println("Hello: "+name);
}
}
-
创建配置文件
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 配置bean
id属性:配置bean的名称,该属性值在IOC容器中是唯一的
class属性:配置bean的全类名,Spring会利用反射技术实例化该bean
-->
<bean id="helloWorld" class="com.atguigu.spring.helloworld.HelloWorld">
<!-- 通过property标签给bean的属性赋值 -->
<property name="name" value="Spring"></property>
</bean>
</beans>
-
测试
@Test
public void test1(){
ClassPathXmlApplicationContext app =
new ClassPathXmlApplicationContext("spring-helloworld.xml");
HelloWorld h = (HelloWorld) app.getBean("helloWorld");
h.sayHello();
}
c. Bean依赖的注入类型
-
集合类型(List(String))
<bean name="address" class="com.xjl.entity.Address">
<property name="address" value="hubei"></property>
<property name="email">
<list>
<value>123@123.com </value>
<value>456@123.com </value>
<value>789@123.com </value>
<value>024@123.com </value>
<!-- <ref bean="user"/> -->
</list>
</property>
</bean>
-
集合数据类型( Map<String,User> )
<bean id="helloWorld" class="com.xjl.HelloWorld">
<property name="name" value="xiaojunlei"></property>
<property name="address" ref="address"></property>
<property name="tel">
<map>
<entry key="QQ" value="121212"></entry>
<entry key="Wechat" value="1024"></entry>
<!-- <entry key="address" value-ref="address"></entry>-->
</map>
</property>
</bean>
-
集合数据类型(Properties)的注入
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
<property name="userMap">
<map>
<entry key="user1" value-ref="u1"/>
<entry key="user2" value-ref="u2"/>
</map>
</property>
</bean>
d. p命名空间注入
7. Spring 配置数据源
常见数据源(连接池)DBCP、C3P0、Druid
spring bean创建外部引用
应用外部文件属性
-
创建properties属性文件
prop.userName=root
prop.password=root
prop.url=jdbc:mysql:///test
prop.driverClass=com.mysql.jdbc.Driver
-
引入context名称空间
xmlns:context="http://www.springframework.org/schema/context"
-
指定properties属性文件的位置
<!-- 指定properties属性文件的位置 -->
<!-- classpath:xxx 表示属性文件位于类路径下 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
-
从properties属性文件中引入属性值
<!-- 从properties属性文件中引入属性值 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${prop.userName}"/>
<property name="password" value="${prop.password}"/>
<property name="jdbcUrl" value="${prop.url}"/>
<property name="driverClass" value="${prop.driverClass}"/>
</bean>
8. 引入其他配置文件
<import resource="applicationContext-xxx.xml"/>
9. 基于注解配置Bean
a. 注解标识组件
1) 普通组件:@Component
标识一个受Spring IOC容器管理的组件。使用在类上,用户实例化Bean。
2) 持久化层组件:@Repository
标识一个受Spring IOC容器管理的持久化层组件。使用在Dao层,用户实例化Bean。
3) 业务逻辑层组件:@Service
标识一个受Spring IOC容器管理的业务逻辑层组件。使用在Server层,用户实例化bean。
4) 表述层控制器组件:@Controller
标识一个受Spring IOC容器管理的表述层控制器组件。使用在Web层用户实例化bean。
5) @value
普通值注入
6) @Scope
标注bean的作用范围
7) @PreDestroy
标注该方法时bean的初始化方法
8) @PostDestroy
标注该方法时bean的销毁方法
9) @Autowired
使用于根据依赖类型注入。需要注入方法、属性的类型。
10) @Qualifier
结合Autowired一起使用根据名称进行依赖注入
11) @Resource
相当于@Autowired+@Qualifier,按照名称进行注入
@Component取代<bean class="">
@Component(“id”) 取代 <bean id="" class="">
web开发,提供3个@Component注解衍生注解(功能一样,只是细化了)取代
-
@Repository :dao层
-
@Service:service层
-
@Controller:web层
只写@autowired 按照数据类型从spring容器中进行匹配
@Qualifier 按照id的值重容器中进行匹配,需要结合autowired
@Resource(name="userDaoImp")作用相当于@autowired+@Qualifier
b. 组件扫描
组件被上述注解标识后还需要通过Spring进行扫描才能够侦测到。
-
指定被扫描的package
<context:component-scan base-package="com.example.component"/>
<!--resource-pattern属性过滤特定的类 -->
<context:component-scan base-package="com.example.component" resource-pattern="autowire/*.class"/>
-
过滤只扫描的类
注意,修改默认的扫描方式为false,因为默认的为全盘扫描
<context:component-scan base-package="com.example.component"
use-default-filters="false">
<!--
type为annotation expression为注解的全类名
type为assignable expression为接口或实现类的全类名
-->
<context:include-filter type="annotation" expression="com.xjl.xxl">
</context:include-filter>
</context:component-scan>
-
过滤不扫描的类
<context:component-scan base-package="com.example.component">
<!--
type为annotation expression为注解的全类名,一般为import的全类名
type为assignable expression为接口或实现类的全类名
-->
<context:exclude-filter type="annotation" expression="com.xjl.xxl">
</context:include-filter>
</context:component-scan>
c. 依赖注入
@Component默认会自动将类名当做id,且首字母小写
普通值注入
@Component("user")
public class User {
// 普通纸注入
@Value("admin")
private String username;
@Value("123456")
private String password;
-
应用值注入
@Component("user")
public class User {
@Autowired
private Address addr;
-
按照名称注入应用值
@Component("addressId")
public class User {
@value(""hubei)
private String addr;
@Component("user")
public class User {
@Autowired
@Qualifier(“addressId”)
private Address addr;
-
按照名称注入方式2,@Resource(name=“名称”)
@Component("user")
public class User {
@Resource(name="addressId")
private Address addr;
-
生命周期
初始化:@PostConstruct 销毁:@PreDestroy
@PostConstruct
public void init(){
System.out.println("初始化");
}
@PreDestroy
public void destroy(){
System.out.println("销毁");
}
-
引入外部文件属性(properties)
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
d. Spring 新注解
使用上面的注解不能全面替代xml配置文件,如:
-
非自定义的bean, <bean>
-
加载properties文件, context:property-placeholder
-
组件扫描,context:component-scan
-
引入其他文件,<import>
新注解
1)@Configuration
指定当前类为spring配置类,当创建容器时会从该类上加载注解。标志当前类为spring的核心配置类。
2)@ComponentScan
指定spring初始化容器时要扫描的包
3)@Bean
标注将该方法的返回值存储到spring容器中
4)@PropertySource
用于加载properties 文件中的配置
5) @Import
用户导入其他配置类
10. Spring 集成Junit
问题: 在测试类中,每个方法都要进行配置文件的设置,及getBean获取bean。
解决: 让SpringJunit负责创建Spring容器,但是需要将配置文件的名称告诉它。将需要进行测试Bean直接在测试类中进行注入
步骤:
①导入spring集成Junit的坐标(junit 4.12及以上版本)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
②使用@Runwith注解替换原来的运行期(指定内核)
③使用@ContextConfiguration指定配置文件或配置类
④使用@Autowired注入需要测试的对象
⑤创建测试方法进行测试
@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration("classpath:spring-anno.xml") // 使用xml配置文件
@ContextConfiguration(classes = {SpringConfiguration.class}) //使用Java类配置文件
public class SpringJunit {
@Autowired
private UserServer userServer;
@Test
public void test1(){
userServer.save();
}
}
11. AOP面向切面编程
AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式和运行期 动态代理实现程序功能的统一维护的一种技术。
AOP 是 OOP 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
动态代理:在不修改源码的情况下,对原有功能进行增强。实现程序之间的松耦合。
优势:
作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强
优势:减少重复代码,提高开发效率,并且便于维护
底层实现
实际上,AOP 的底层是通过 Spring 提供的的动态代理技术实现的。在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。
常用的动态代理技术
JDK 代理 : 基于接口的动态代理技术
cglib 代理:基于父类的动态代理技术
a. JDK动态代理
目标接口
public interface TargetInterface {
public void method();
}
目标类
public class Target {
public void method() {
System.out.println("Target running....");
}
}
动态代理代码
Target target = new Target(); //创建目标对象
//创建代理对象
TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(target.getClass()
.getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("前置增强代码...");
Object invoke = method.invoke(target, args);
System.out.println("后置增强代码...");
return invoke;
}
}
);
测试
proxy.method();
b. cglib动态代理
目标类
public class Target {
public void method() {
System.out.println("Target running....");
}
}
动态代理代码
Target target = new Target(); //创建目标对象
Enhancer enhancer = new Enhancer(); //创建增强器
enhancer.setSuperclass(Target.class); //设置父类
enhancer.setCallback(new MethodInterceptor() { //设置回调
@Override
public Object intercept(Object o, Method method, Object[] objects,
MethodProxy methodProxy) throws Throwable {
System.out.println("前置代码增强....");
Object invoke = method.invoke(target, objects);
System.out.println("后置代码增强....");
return invoke;
}
});
Target proxy = (Target) enhancer.create(); //创建代理对象
测试
proxy.method();
c. AOP相关概念
Spring 的 AOP 实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。
在正式讲解 AOP 的操作之前,我们必须理解 AOP 的相关术语,常用的术语如下:
-
Target(目标对象):代理的目标对象
-
Proxy (代理):一个类被 AOP 织入增强后,就产生一个结果代理类
-
Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。可以被增强的方法叫做连接点。
-
Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义,连接点的一部分。
-
Advice(通知/ 增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知
-
Aspect(切面):是切入点和通知(引介)的结合。切点+通知
-
Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入
d. 开发明确事项
-
编写核心业务代码(目标类的目标方法)
-
编写切面类,切面类中有通知(增强功能方法)
-
在配置文件中,配置织入关系,即将哪些通知与哪些连接点进行结合
-
Spring会自定判断使用哪种代理方法
e. 基于XML的AOP开发
步骤:
①导入 AOP 相关坐标
<!--导入spring的context坐标,context依赖aop-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<!-- aspectj的织入 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
②创建目标接口和目标类(内部有切点)
如上。
③创建切面类(内部有增强方法)
public class MyAspect {
//前置增强方法
public void before(){
System.out.println("前置增强方法。。。");
}
}
④将目标类和切面类的对象创建权交给 spring
<!--目标对象-->
<bean id="target" class="com.xjl.aop.Target"></bean>
<!--切面对象-->
<bean id="myAspect" class="com.xjl.aop.MyAspect"></bean>
<!---配置织入,告诉spring框架,那些方法(切点)需要进行那些增强(前置,后置)-->
<aop:config>
<!-- 切面声明-->
<aop:aspect ref="myAspect">
<!--切面:切点+通知
method
-->
<aop:before method="before" pointcut="execution(public void com.xjl.aop.Target.save())"></aop:before>
</aop:aspect>
</aop:config>
⑤在 applicationContext.xml 中配置织入关系
⑥测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring.xml")
public class AOPTest {
@Autowired
private TargetInterface target;
@Test
public void test1(){
target.save();
}
}
f. 切点表达式
execution([修饰符] 返回值类型 包名.类名.方法名(参数))
-
访问修饰符可以省略
-
返回值类型、包名、类名、方法名可以使用星号* 代表任意
-
包名与类名之间一个点 . 代表当前包下的类,两个点 .. 表示当前包及其子包下的类
-
参数列表可以使用两个点 .. 表示任意个数,任意类型的参数列表
例
execution(public void com.itheima.aop.Target.method())
execution(void com.itheima.aop.Target.*(..))
execution(* com.itheima.aop.*.*(..))
execution(* com.itheima.aop..*.*(..))
execution(* *..*.*(..))
g. 通知类型
<aop:通知类型 method=“切面类中方法名” pointcut=“切点表达式"></aop:通知类型>
h. 切点表达式的抽取
当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用 pointcut-ref 属性代替 pointcut 属性来引用抽取后的切点表达式。
<aop:config>
<!--引用myAspect的Bean为切面对象-->
<aop:aspect ref="myAspect">
<aop:pointcut id="myPointcut" expression="execution(* com.itheima.aop.*.*(..))"/>
<aop:before method="before" pointcut-ref="myPointcut"></aop:before>
</aop:aspect>
</aop:config>
i. 基于注解AOP
步骤
①创建目标接口和目标类(内部有切点)
②创建切面类(内部有增强方法)
③将目标类和切面类的对象创建权交给 spring
@Aspect
配置当前类是一个切面
@Before(value="execution(void com.itheima.aop.Target.*(..))")
④在切面类中使用注解配置织入关系
⑤在配置文件中开启组件扫描和 AOP 的自动代理
<content:component-scan base-package="com.xjl.anno"/>
<!--aop的自动代理-->
<aop:aspectj-autoproxy/>
⑥测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-anno.xml")
public class AnnoTest {
@Autowired
private TargetInterface target;
@Test
public void test1() {
target.save();
}
}
切点表达式的抽取
@Component("myAspect")
@Aspect
public class MyAspect {
@Before("MyAspect.myPoint()")
public void before(){
System.out.println("前置代码增强.....");
}
@Pointcut("execution(* com.itheima.aop.*.*(..))")
public void myPoint(){}
}
12. spring jdbc模板
spring产生模板对象代码实现(应用)
<!--数据源对象-->
<bean id="dataSourceObject" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql:///test"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!--jdbc模板对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSourceObject"/>
</bean>
编程式事务控制
声明式事务控制(基于xml和基于注解)
底层就是AOP。
自动装配
1) 需求
Controller组件中往往需要用到Service组件的实例,Service组件中往往需要用到 Repository组件的实例。Spring可以通过注解的方式帮我们实现属性的装配。
2) 实现依据[了解]
在指定要扫描的包时,context:component-scan 元素会自动注册一个bean的后置处 理器:AutowiredAnnotationBeanPostProcessor的实例。该后置处理器可以 自动装配标
记了@Autowired、@Resource或@Inject注解的属性。
3) @Autowired注解
①根据类型实现自动装配。
②构造器、普通字段(即使是非public)、一切具有参数的方法都可以应用@Autowired 注解
③默认情况下,所有使用@Autowired注解的属性都需要被设置。当Spring找不到匹 配的bean装配属性时,会抛出异常。
④若某一属性允许不被设置,可以设置@Autowired注解的required属性为 false
⑤默认情况下,当IOC容器里存在多个类型兼容的bean时,Spring会尝试匹配bean 的id值是否与变量名相同,如果相同则进行装配。如果bean的id值不相同,通过类 型的自动装配将无法工作。此时可以在@Qualifier注解里提供bean的名称。Spring 甚至允许在方法的形参上标注@Qualifiter注解以指定注入bean的名称。 ⑥@Autowired注解也可以应用在数组类型的属性上,此时Spring将会把所有匹配的bean进行自动装配。
⑦@Autowired注解也可以应用在集合属性上,此时Spring读取该集合的类型信息,然后自动装配所有与之兼容的bean。
⑧@Autowired注解用在java.util.Map上时,若该Map的键值为String,那么 Spring将自动装配与值类型兼容的bean作为值,并以bean的id值作为键。
4) @Resource
@Resource注解要求提供一个bean名称的属性,若该属性为空,则自动采用标注处的变量或方法名作为bean的名称。
5) @Inject
@Inject和@Autowired注解一样也是按类型注入匹配的bean,但没有reqired属性。