Spring5
简介
Spring是Java EE编程领域的一个轻量级开源框架,该框架由一个叫Rod Johnson的程序员在 2002 年最早提出并随后创建,是为了解决企业级编程开发中的复杂性,实现敏捷开发的应用型框架 。Spring是一个开源容器框架,它集成各类型的工具,通过核心的Bean factory实现了底层的类的实例化和生命周期的管理。在整个框架中,各类型的功能被抽象成一个个的 Bean,这样就可以实现各种功能的管理,包括动态加载和切面编程。
参考网站:
第一个Spring程序
创建一个maven项目
在pom.xml里导入依赖
1
2
3
4
5
6
7<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
</dependencies>创建一个类,比如我的HelloSpring类
1
2
3
4
5
6
7
8
9
10
11
12
13public class HelloSpring {
private String str;
public String getStr(){
return str;
}
public void setStr(String str){
this.str = str;
}
public String toString(){
return "{"+"str="+str+"}";
}
}在resources资源目录下配置applicationContext.xml
1
2
3
4
5
6
7
8
9
10
11
<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">
<!--使用Spring来创建对象,这些对象都称为bean-->
<bean id ="hello" class="com.chen.pojo.HelloSpring">
<property name="str" value=" Hello Spring"/>
</bean>
</beans>测试类
1
2
3
4
5
6
7
8public class test01 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
HelloSpring hello = context.getBean("hello",HelloSpring.class);
System.out.println(hello.toString());
}
}
//得到结果,输出 {str= Hello Spring}
Spring配置
别名
为创建的对象起别名,即为bean的id起别名
1 | <alias name="userserviceimpl" alias="user"></alias> |
Bean
以上面的例子为例
1 | <bean id ="hello" class="com.chen.pojo.HelloSpring" name ="user,u1,u2"> |
id为bean的标识符,即创建的对象名
class为对象对所对应的类名,注意是全限定名
name为别名,与alias差不多,可以起多个别名
property属性,为str这个参数赋值
import
合并xml配置文件。
依赖注入
依赖注入DI,具体说法可参考我之前写的有关IOC的文章
构造器注入
使用无参构造器创建对象
这种是Spring默认的创建对象的方式,在上述的Spring例子当中,HelloSpring的bean创建对象是用的无参构造器,若没有了无参构造器就会报错。使用有参构造器创建对象
下标
1
2
3
4<bean id ="hello" class="com.chen.pojo.HelloSpring">
<constructor-arg index="0" value="helloSpring">;
<!--从0开始,依次为HelloSpring的有参构造器赋值-->
</bean>类型
1
2
3
4<bean id ="hello" class="com.chen.pojo.HelloSpring">
<constructor-arg type="String" value="helloSpring">;
<!--按照类型来赋值,但相同类型多了就不行了,还是不推荐这种-->
</bean>参数名
1
2
3
4<bean id ="hello" class="com.chen.pojo.HelloSpring">
<constructor-arg name="str" value="helloSpring">;
<!--按构造器的参数名来赋值,即为HelloSpring的构造器参数str赋值-->
</bean>当bean被注册,加载到该配置文件,即使没有用到这个对象,也会生成。
Set方式注入
注入到对象中的属性,例如我的这个例子。
1 | public class Student { |
基本数据类型和String类型的注入
1
2
3<bean id="student" class="com.chen.pojo.Student">
<property name="name" value="student1"/>
</bean>引用类型的注入
1
2
3
4
5<!--先注册好这个引用类型-->
<bean id="adress" class="com.chen.pojo.Address"/>
<bean id="student" class="com.chen.pojo.Student">
<property name="address" ref="adress"/>
</bean>数组的注入
1
2
3
4
5
6
7
8
9
10<bean id="student" class="com.chen.pojo.Student">
<property name="books">
<array>
<value>a</value>
<value>b</value>
<value>c</value>
<value>d</value>
</array>
</property>
</bean>List的注入
1
2
3
4
5
6
7
8<bean id="student" class="com.chen.pojo.Student">
<property name="hobbies">
<list>
<value>music</value>
<value>game</value>
</list>
</property>
</bean>Map的注入
1
2
3
4
5
6
7
8
9
10<bean id="student" class="com.chen.pojo.Student">
<property name="card">
<map>
<entry key="1" value="a"/>
<entry key="2" value="b"/>
<entry key="3" value="c"/>
<entry key="4" value="d"/>
</map>
</property>
</bean>Set的注入
1
2
3
4
5
6
7
8
9<bean id="student" class="com.chen.pojo.Student">
<property name="games">
<set>
<value>kkk</value>
<value>sss</value>
<value>www</value>
</set>
</property>
</bean>null注入即空值注入
1
2
3
4
5<bean id="student" class="com.chen.pojo.Student">
<property name="ll">
<null/>
</property>
</bean>Properties的注入
1
2
3
4
5
6
7
8<bean id="student" class="com.chen.pojo.Student">
<property name="info">
<props>
<prop key="s">111</prop>
<prop key="ww">333</prop>
</props>
</property>
</bean>注入的时候是通过你创建的set方法注入的,所以创建类的时候要写上set方法,养成习惯get方法也写上,为了更好的测试,就重写toString方法 拓展方式注入
P命名空间的注入
想要使用这种方法,先得在bean元素上加入p的命名空间,如下
1 |
|
1 | <bean id="adress" class="com.chen.pojo.Address" p:address="this" p:a="18"/> |
这种方式的注入是简单类型的注入,引用类型也可以,类似set注入,但能支持注入的类型少。
C命名空间注入
类似P命名空间,用前需要在bean元素上加入c的命名空间,如下
1 |
|
1 | <bean id="adress" class="com.chen.pojo.Address" c:address="this" c:a="18"/> |
这种方式的是通过构造器注入的,想要注入的属性就要存在这样的有参构造器。注意因为创建对象要用到无参构造器,所以不要漏写。
Bean的作用域(Scope)
单例模式
单例模式是指每次创建的bean都是同一个,这也是Spring默认的模式,比如
1 | <!--singleton表示单例模式,不写scope也默认为单例--> |
1 | //测试类,两个get的对象都是一样的 |
单例模式表示bean只会创建一个对象,每次get的都是同一个
原型模式
原型模式就和单例模式相反,每次get的都不是同一个。
1 | <!--每次创建都不是同一个对象--> |
1 | //测试类,两个get的对象都是一样的 |
原型模式表示每次创建的bean都不是同一个对象
Spring的自动装配
首先为了更好的测试,先创建两个类,之后的例子就从这两个类中测试。
1 | public class User { |
1 | public class Score { |
自动装配是从bean的autowire属性配置的。
byName的自动装配
byName的自动装配是从类中寻找具有相同属性的类,然后装配到bean里,比如在User中有Score类创建的对象,而且有set方法,所以可以自动将Score的对象装配到User类里。
1 |
|
1 | public class test01 { |
是按照User类里的Set方法寻找的,比如Score类在bean里注册的id为score,那User类里就要有setScore这个set方法,将set去掉,首字母小写,然后与id对应一致才能找的到,这是byName的自动装配。
是按照User类里的Set方法寻找的,比如Score类在bean里注册的id为score,那User类里就要有setScore这个set方法,将set去掉,首字母小写,然后与id对应一致才能找的到,这是byName的自动装配。
byTypee的自动装配
1 |
|
1 | public class test01 { |
byType是寻找类里属性类型相同的bean,所以不关id的事情了,id都可以省略。弊端就是当一个类注册了两个bean的时候,byType不知道选择哪一个就会报错。
用注解实现自动装配
首先想用注解先要导入context约束和注解的支持,如下
1 |
|
@Autowired注解
这个注解可以放在属性名上,也可以放在set方法上,放在属性名上,连set方法都可以不用写,当然是引用的类里要有set方法。
1 | <bean class="com.chen.pojo.Score"> |
1 | public class User { |
@Autowired里面有个属性是required,默认为true,表示不能为空值,可以手动关闭@Autowired(required = falser)。
@Autowired是通过byType模式来寻找的,若是一个类有多个bean才会选择用byName来自动装配。
要是多个bean的id都不符合byName的时候,就需要使用@Qualifier(value="id")来配合使用,value的值为哪个bean的id,就使用哪个bean自动装配。
1 | <bean id="score1" class="com.chen.pojo.Score"> |
使用注解开发
想使用注解来进行开发,先要确定好自己的配置。
导入aop的包(之前导入的依赖包含了aop的包),注解使用不了就确定一下有没有aop的包导入
导入context的约束,开启支持注解
还要指定扫描的包,是包里的注解生效,比如例子的包
1
<context:component-scan base-package="com.chen.pojo"/>
注册bean
在xml中注册bean
1 | <bean id="score" class="com.chen.pojo.Score"> |
使用注解来注册bean
1 |
|
使用了@Component注解,会将这个类装配。可以指定id,在没有指定id的时候,这个类的bean的id就是当前类名首字母小写的字符串。
指定id可以这样写到@Component(“xxx”)。
@Component 的三个衍生注解,在 web 开发中,会按照 mvc 三层架构分层。
- dao 层@Repository
- service 层@Service
- web 层@Controller
这四个注解功能都是一样的。
属性注入
1 |
|
属性注入可以使用@Value(“”)的注解,当然简单的才会使用注解,复杂一点的注入还是xml配置注解好一点。
自动装配
就是之前得用注解实现自动装配
作用域
1 |
|
作用域可以使用@Scope(““),里面是模式,常用的就是singleton和prototype,默认也是singleton。
使用Java的方式配置Spring
先看demo吧
用户类
1
2
3
4
5
6
7
8
9
10
11public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}Java配置类
1
2
3
4
5
6
7
public class pojoconfig {
public User getUser(){
return new User();
}
}测试类
1
2
3
4
5
6
7public class test01 {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(pojoconfig.class);
User user = context.getBean("getUser",User.class);
System.out.println(user.getName());
}
}一个个分析吧。
用户类没什么区别,主要是Java配置类。
@Configuration表示这个类是个配置类,类似于xml。
@Bean表示bean的配置。方法名就相当于id。
AOP
关于AOP的介绍就放到另一篇文章里。AOP
这里就讲讲spring中AOP的具体实现。
首先要导入依赖,和aop的约束
1 | <dependency> |
1 |
|
使用Spring的接口来实现
首先定义好 接口,并创建好真实对象,即接口实现类
1
2
3
4
5
6public interface userService {
void add();
void delete();
void update();
void select();
}1
2
3
4
5
6
7
8
9
10
11
12
13
14public class userServiceImpl implements userService{
public void add() {
System.out.println("增加了一个用户");
}
public void delete() {
System.out.println("删除了一个用户");
}
public void update() {
System.out.println("修改了一个用户");
}
public void select() {
System.out.println("查询了一个用户");
}
}定义一个功能类
1
2
3
4
5public class log implements MethodBeforeAdvice {
public void before(Method method, Object[] objects, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
}
}method表示目标对象的方法,objects表示参数,target目标对象。实验的例子是使用MethodBeforeAdvice这个接口,即在目标方法调用之前调用的Advice通知。
MethodBeforeAdvice 前置通知
AfterReturningAdvice 后置通知,有返回值
MethodInterceptor 环绕通知
ThrowsAdvice 异常通知然后就要在applicationContext.xml里配置了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<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="userServiceImpl" class="com.chen.pojo.userServiceImpl"></bean>
<bean id ="log" class="com.chen.pojo.log"></bean>
<aop:config>
<!--定义切入点,expression为执行的位置-->
<aop:pointcut id="p" expression="execution(* com.chen.pojo.userServiceImpl.*(..))"/>
<!--执行环绕,advice-ref表示哪一个通知,pointcut-ref表示切入位置-->
<aop:advisor advice-ref="log" pointcut-ref="p"></aop:advisor>
</aop:config>
</beans>这里来解析一下execution的表达式。* com.chen.pojo.userServiceImpl..* .*(..)
- execution()为表达式主体,里面为表达式
- 第一个 * 表示返回的类型,*表示任意类型的返回值
- 表示需要拦截的包,即真实对象所在的位置
- 包名后面的,,*表示当前包以及子包
- 第二个 * 表示方法,*表示所有方法
- (..)表示方法可以有任意的参数
测试类
1
2
3
4
5
6
7
8
9
10
11public class test01 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
userService userService = context.getBean("userServiceImpl", userService.class);
userService.add();
userService.delete();
userService.update();
userService.select();
}
}注意这里的用的是接口而不是实现类。
使用自定义类来实现
定义接口和实现类,和上述一样
定义一个通知类
1
2
3
4
5public class log {
public void before(){
System.out.println("方法执行");
}
}配置xml文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<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="userServiceImpl" class="com.chen.pojo.userServiceImpl"></bean>
<bean id = "log" class="com.chen.pojo.log"/>
<aop:config>
<!--自己定义一个切面,引用刚刚的类-->
<aop:aspect ref="log">
<!--定义切入点-->
<aop:pointcut id="point" expression="execution(* com.chen.pojo.userServiceImpl.*(..))"/>
<!--环绕方式,method表示环绕方法,pointcut-ref在哪环绕-->
<aop:before method="before" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
</beans>这种方法还是不够好,简单的插入可以使用这种。
使用注解来实现
使用注解来实现跟自定义类实现其实差不多。
定义接口和实现类,和上述一样
定义通知类
1
2
3
4
5
6
7
8
9//定义切面
public class log {
//定义环绕方式,里面是切入点
public void before(){
System.out.println("方法执行");
}
}然后把这个类注册到spring里,并开启注解支持
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<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="userServiceImpl" class="com.chen.pojo.userServiceImpl"></bean>
<bean id ="log" class="com.chen.pojo.log"/>
<!--开启注解支持-->
<aop:aspectj-autoproxy/>
</beans>开启注解支持还能设置动态代理模式
<aop:aspectj-autoproxy proxy-target-class=”false”/>值为false表示使用JDK动态代理(默认),为true表示使用cglib动态代理
Spring与Mybatis整合
导入依赖
1 |
|
配置spring的xml
1 |
|
这个xml是可以代替原来Mybatis的配置文件,以后使用可以直接通过import导入就好了。
事务
导入约束
用Spring事务时先导入约束,如下
1 |
|
配置事务通知
1 | <!--声明式事务--> |
配置事务切入
1 | <!--声明式事务--> |
后话
spring终于结束了,说实话spring相对Mybatis还是简单很多的,但细节还是挺多的,学了几天,前面又忘了,还是得多看看细节才行。emm,我的笔记也比较水,还有一些细节还没写出来,看得云里雾里得也挺正常,大概就看看复习一下就好了。end。下个框架见。
还没写完—