概述
Why Spring
如果我们有一个接口
package dao;
public interface userdao {
void select();
}
有两个实现方法
package dao;
public class mysqlimpl implements userdao {
@Override
public void select() {
System.out.println("mysql");
}
}
package dao;
public class mssqlimpl implements userdao {
@Override
public void select() {
System.out.println("mssql");
}
}
我们如果要使用的话
package dao;
import org.junit.Test;
public class test1 {
@Test
public void withoutSpring(){
userdao mysqldaoimpl = new mysqlimpl();
mysqldaoimpl.select();
}
}
就是前面概述中所说直接new了一个对象,如果在后面想换查询时还要修改代码,效率不高,而且这样写的话控制权全在开发者手里,而不是在用户的手里。
Spring使用
快速创建一个maven工程,然后添加spring依赖即可
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
在resources下新建一个bean.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="mysqlimpl" class="dao.mysqlimpl"/>
<bean id="mssqlimpl" class="dao.mssqlimpl"/>
</beans>
添加如下的test方法即可
@Test
public void SpringTest(){
ApplicationContext cl = new ClassPathXmlApplicationContext("Bean.xml"); //传入xml文件
userdao mysqldaoimpl = (userdao)cl.getBean("mysqldaoimpl"); //获取mysqlimpl方法
mysqldaoimpl.select();//调用mysqlimpl方法
}
- 创建 ApplicationContext 对象时,我们使用了 ClassPathXmlApplicationContext 类。该类用于加载 Spring 配置文件、创建和初始化所有对象,也就是下面配置文件中提到的 Bean。
- ApplicationContext.getBean() 方法用来获取 Bean,该方法返回值类型为 Object,通过强制类型转换为 userdao 的实例对象,根据该对象调用类中的方法。
IoC容器
BeanFactory 容器
ApplicationContext 容器
ApplicationContext 接口有两个常用的实现类:
ClassPathXmlApplicationContext
该类从类路径 ClassPath 中寻找指定的 XML 配置文件,并完成 ApplicationContext 的实例化工作,具体如下所示。
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(String configLocation);
在上述代码中,configLocation 参数用于指定 Spring 配置文件的名称和位置,如 Beans.xml。
FileSystemXmlApplicationContext
该类从指定的文件系统路径中寻找指定的 XML 配置文件,并完成 ApplicationContext 的实例化工作,具体如下所示。
ApplicationContext applicationContext = new FileSystemXmlApplicationContext(String configLocation);
它与 ClassPathXmlApplicationContext 的区别是:在读取 Spring 的配置文件时,FileSystemXmlApplicationContext 不会从类路径中读取配置文件,而是通过参数指定配置文件的位置。即 FileSystemXmlApplicationContext 可以获取类路径之外的资源,如“F:/workspaces/Beans.xml”。
Bean
Bean标签作用:
属性名称 | 描述 |
---|---|
id | Bean 的唯一标识符,Spring 容器对 Bean 的配置和管理都通过该属性完成。id 的值必须以字母开始,可以使用字母、数字、下划线等符号。 |
name | name 属性中可以为 Bean 指定多个名称,每个名称之间用逗号或分号隔开。Spring 容器可以通过 name 属性配置和管理容器中的 Bean。 |
class | 该属性指定了 Bean 的具体实现类,它必须是一个完整的类名,即类的全限定名。 |
scope | 用于设定 Bean 实例的作用域,属性值可以为 singleton(单例)、prototype(原型)、request、session 和 global Session。其默认值是 singleton |
init-method | 容器加载 Bean 时调用该方法,类似于 Servlet 中的 init() 方法 |
constructor-arg | 可以使用此元素传入构造参数进行实例化 |
依赖注入
Spring 容器在创建被调用者的实例时,会自动将调用者需要的对象实例注入给调用者,调用者通过 Spring 容器获得被调用者实例,这称为依赖注入(Dependency Injection,DI)。我们不需要再创建示例了(New)
构造注入
使用类中的构造函数,让 spring 框架来为我们注入。
Eg:
package User;
import java.util.Date;
public class Student {
private String name;
private Integer age;
private Date date;
public Student() {
}
public Student(String name, Integer age, Date date) {
this.name = name;
this.age = age;
this.date = date;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
@Override
public String toString() {
return "Perpon{" +
"name='" + name + '\'' +
", age=" + age +
", date=" + date +
'}';
}
}
xml添加如下
<bean id="person" class="User.Student">
<constructor-arg name="name" value="xiaoming"/>
<constructor-arg name="age" value="18"/>
<constructor-arg name="date" ref="now"/>
</bean>
<bean id = "now" class="java.util.Date"/>
即可测试
@Test
public void DIConstrutor(){
ApplicationContext cl = new ClassPathXmlApplicationContext("Bean.xml"); //传入xml文件
Student person = (Student) cl.getBean("person");
System.out.println(person);
}
在 标签中,包含 ref、value、type、index 等属性。value 属性用于注入基本数据类型以及字符串类型的值;ref 属性用于注入已经定义好的 Bean;type 属性用来指定对应的构造函数,当构造函数有多个参数时,可以使用 index 属性指定参数的位置,index 属性值从 0 开始。
set注入
<bean id="person" class="User.Student">
<property name="name" value="xiaoming"/>
<property name="age" value="18"/>
<property name="date" ref="now"/>
</bean>
<bean id = "now" class="java.util.Date"/>
在 标签中,包含 name、ref、value 等属性。name 用于指定参数名称;value 属性用于注入基本数据类型以及字符串类型的值;ref 属性用于注入已经定义好的 Bean。
Bean自动装配
使用自动装配需要配置 元素的 autowire 属性。autowire 属性有五个值,具体说明如下表所示。
名称 | 说明 |
---|---|
no | 默认值,表示不使用自动装配,Bean 依赖必须通过 ref 元素定义。 |
byName | 根据 Property 的 name 自动装配,如果一个 Bean 的 name 和另一个 Bean 中的 Property 的 name 相同,则自动装配这个 Bean 到 Property 中。 |
byType | 根据 Property 的数据类型(Type)自动装配,如果一个 Bean 的数据类型兼容另一个 Bean 中 Property 的数据类型,则自动装配。 |
constructor | 类似于 byType,根据构造方法参数的数据类型,进行 byType 模式的自动装配。 |
autodetect(3.0版本不支持) | 如果 Bean 中有默认的构造方法,则用 constructor 模式,否则用 byType 模式。 |
byNameEg:
package User;
public class Student {
private String name;
private Play play;
private Study study;
public Student() {
}
public Student(String name, Play play, Study study) {
this.name = name;
this.play = play;
this.study = study;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Study getStudy() {
return study;
}
public void setStudy(Study study) {
this.study = study;
}
public Play getPlay() {
return play;
}
public void setPlay(Play play) {
this.study = study;
}
}
xml添加(XML 文件中 Bean 的 id 必须与类中的属性名称相同)
<bean id="person" class="User.Student" autowire="byName">
<property name="name" value="xiaoming"/>
</bean>
<bean id="play" class="User.Play"/>
<bean id="study" class="User.Study"/>
测试一下
@Test
public void byName(){
ApplicationContext cl = new ClassPathXmlApplicationContext("Bean.xml"); //传入xml文件
Student person = (Student) cl.getBean("person");
System.out.println(person.getName());
person.getPlay().method();
person.getStudy().method();
}
基于注解装配Bean
如果还是嫌写Bean太过麻烦,可以用注解的方式,详细用法可以见:基于注解装配Bean
Aop
AOP 的全称是“Aspect Oriented Programming”,即面向切面编程。
简单的说,AOP 的作用就是保证开发者在不修改源代码的前提下,为系统中的业务组件添加某种通用功能。AOP 就是代理模式的典型应用。
why
相关术语
横切关注点:对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点
名称 | 说明 |
---|---|
Joinpoint(连接点) | 指那些被拦截到的点,在 Spring 中,指可以被动态代理拦截目标类的方法。 |
Pointcut(切入点) | 指要对哪些 Joinpoint 进行拦截,即被拦截的连接点。 |
Advice(通知) | 指拦截到 Joinpoint 之后要做的事情,即对切入点增强的内容。 |
Target(目标) | 指代理的目标对象。 |
Weaving(植入) | 指把增强代码应用到目标上,生成代理对象的过程。 |
Proxy(代理) | 指生成的代理对象。 |
Aspect(切面) | 切入点和通知的结合。(也是对横切关注点的抽象) |
AOP实现(AspectJ)
导入相关依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
<scope>runtime</scope>
</dependency>
接口
package AOP;
public interface service {
void add();
void delete();
void update();
void qurey();
}
实现
package AOP;
public class serviceImpl implements service {
@Override
public void add() {
System.out.println("add");
}
@Override
public void delete() {
System.out.println("delete");
}
@Override
public void update() {
System.out.println("update");
}
@Override
public void qurey() {
System.out.println("query");
}
}
不修改代码的情况下,想增强一下它的功能的话,以前会直接使用动态代理来实现,书写起来很麻烦。这里就可以使用到aop。
这里再来定义2个方法,分别是方法执行前执行的方法,和方法执行后执行的方法。也叫做前置增强和后置增强
前置:
package AOP;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class log implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(o.getClass().getName()+"的"+method.getName()+"方法执行了");
}
}
后置
package AOP;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class afterlog implements AfterReturningAdvice{
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("执行了"+method.getName()+"执行结果为"+o);
}
}
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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- Spring aop 命名空间-->
<bean id="service" class="AOP.serviceImpl"/>
<bean id="log" class="AOP.log"/>
<bean id="afterlog" class="AOP.afterlog"/>
<aop:config>
<!-- 声明切入点,并且使用expression表达式设置需要执行的位置-->
<aop:pointcut id="pointcut" expression="execution(* AOP.serviceImpl.*(..))"/>
<!-- advice-ref:设置需要切入的功能,pointcut-ref:设置需要切入的点 -->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterlog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
Test
@Test
public void AOP(){
ApplicationContext cl = new ClassPathXmlApplicationContext("Bean_Aspectj.xml");
service s = (service) cl.getBean("service");
s.add();
}
也可以将前置增强和后置增强放到一个类中,详细示例可见:Spring AOP:基于AspectJ XML开发的示例(注意method)
参考链接
Java学习之Spring框架入门篇