狂神说-Spring学习总结
初始Spring
软件开发中的各个版本
Alpha:是内部测试版,一般不向外部发布,会有很多Bug.一般只有测试人员使用。
Beta:也是测试版,这个阶段的版本会一直加入新的功能。在Alpha版之后推出。
RC:(Release Candidate) 顾名思义, 用在软件上就是候选版本。系统平台上就是发行候选版本。RC 版不会再加入新的功能了,主要着重于修复测试中发现的缺陷。
GA : General Availability,正式发布的版本,国外通常用 GA 来标识 release 版本,GA 版本是开发团队认为该版本是稳定版(有的软件可能会标识为 Stable 版本或者 Production 版本,其意思和 GA 相同),可以在较为关键的场合使用,比如生产环境。
snapshots快照版本,代表正在开发中的版本,一般处于开发阶段,0.0.1版本还有功能没有完成,或还有bug还要修复,所以这个阶段一般代码更新比较频繁
release代表比较稳定的发布版本,次迭代的所有功能都已经完成,并且通过了测试之后,就可以发布为0.0.1-Release版本,Release版的一旦发布,就不要再改变代码了,所以如果之后在0.0.1-Release这个版本上发现了bug,需要修复,那么我们应该将0.0.1-Release版本改为0.0.2-SNAPSHOT,然后再修改bug并测试,没问题之后将0.0.2-SNAPSHOT改为0.0.2-Release发布.
简介
-
Spring:春天,给软件行业带来了春天!
-
2002年,首次推出Spring框架的雏形,interface21框架
-
Spring框架以interface21框架为基础经过重新设计,并不断丰富其内涵,于2004年3月24日发布了1.0正式版本。
-
Rod Johnson,Spring framwork创始人,他的专业不是计算机,而是音乐家。
-
Spring理念:使现有技术更容易使用。本身是一个大杂烩,整合了现有的技术框架!
-
SSH : Struct2 + Spring + Hibernate
-
SSM : SpringMVC + Spring + Mybatis
官网:https://docs.spring.io/spring-framework/docs/current/reference/html/overview.html#overview
官方下载地址:https://repo.spring.io/ui/native/release/org/springframework/spring
<dependencies>
<!-- https://mvnrepository.com/artifact/springframework/spring-webmvc -->
<dependency>
<groupId>springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>1.2.6</version>
</dependency>
</dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.9</version>
</dependency>
优点
- Spring是一个开源免费的框架(容器)
- Spring是一个轻量级的,非入侵式的框架
- 控制反转(IOC),面向切面编程(AOP)
- 支持事务处理,对框架整合的支持
总结一句话:Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)
组成
七大模块
拓展
-
Spring Boot
- 一个快速开发的脚手架
- 基于SpringBoot可以快速开发单个服务
- 约定大约配置
-
Spring Cloud
- Spring Cloud是基于SpringBoot实现的
学习SpringBoot的前提是,学习Spring和SpringMVC!
弊端:发展太久了了,违背了原来的理念!配置十分繁琐,人称"配置地狱"!
IOC 理论推导
导包
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
</dependencies>
常规写法
-
UserDao
package com.hopeful.dao; public interface IUserDao { public void getUser(); }
-
UserDaoImpl
package com.hopeful.dao; public class UserDaoImpl implements IUserDao { public void getUser() { System.out.println("默认获取用户"); } }
-
UserService
package com.hopeful.service; import com.hopeful.dao.IUserDao; import com.hopeful.dao.UserDaoImpl; public interface IUserService { public void getUser(); }
-
UserServiceImpl
package com.hopeful.service; import com.hopeful.dao.IUserDao; import com.hopeful.dao.UserDaoImpl; import com.hopeful.dao.UserMySQLImpl; public class UserServiceImpl implements IUserService { // public IUserDao userDao = new UserDaoImpl(); public IUserDao userDao = new UserMySQLImpl(); public void getUser() { userDao.getUser(); } }
-
测试
package com.hopeful.service; public class MyTest { public static void main(String[] args) { IUserService userServie = new UserServiceImpl(); userServie.getUser(); } }
如果突然增加一个实现IUserDao
接口的实现类,则需要修改Service层的代码,十分繁琐,如果代码量特别大,则维护的成本,就十分昂贵了。具体如下:
package com.hopeful.dao;
import com.hopeful.service.IUserService;
public class UserMySQLImpl implements IUserDao {
public void getUser() {
System.out.println("获取SQL用户");
}
}
UserServiceImpl修改为:
package com.hopeful.service;
import com.hopeful.dao.IUserDao;
import com.hopeful.dao.UserDaoImpl;
import com.hopeful.dao.UserMySQLImpl;
public class UserServiceImpl implements IUserService {
// public IUserDao userDao = new UserDaoImpl();
public IUserDao userDao = new UserMySQLImpl();
public void getUser() {
userDao.getUser();
}
}
注入写法
使用Setter接口实现革命性的变化,具体如下:
UserServiceImpl修改为
package com.hopeful.service;
import com.hopeful.dao.IUserDao;
import com.hopeful.dao.UserDaoImpl;
import com.hopeful.dao.UserMySQLImpl;
import java.util.Set;
public class UserServiceImpl implements IUserService {
// public IUserDao userDao = new UserDaoImpl();
// public IUserDao userDao = new UserMySQLImpl();
public IUserDao userDao;
// 利用set方法动态实现对象的绑定
public void setUserDao(IUserDao userDao) {
// 此处必须写this,不然会出现空指针
this.userDao = userDao;
}
public void getUser() {
userDao.getUser();
}
}
测试
package com.hopeful.service;
import com.hopeful.dao.UserMySQLImpl;
public class MyTest {
public static void main(String[] args) {
IUserService userServie = new UserServiceImpl();
((UserServiceImpl)userServie).setUserDao(new UserMySQLImpl());
userServie.getUser();
}
}
原来是程序主动创建对象,也就是需要程序猿主动创建
使用set注入后,程序不再具有主动性,而是变成了被动地接收对象,即现在创建的主动权交给用户,需要哪个创建哪个。
程序耦合性低,便于维护。
总结:这种思想,从本质上解决了问题,我们程序猿不用再去管理对象的创建了,系统的耦合大大降低了,可以更加专注的在业务的实现上。这是IOC的原型。
IOC的本质
控制反转ICO(Inversion Of Control),是一种设计思想,DI(依赖注入)是实现IOC的一种方式。也有人认为DI只是IOC的另一种说法。没有IOC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后,将对象的创建转移给第三方,个人文伟所谓控制反转就是:获得依赖对象的方式反转了。
Spring中IOC容器
采用XML方式配置 Bean 的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者何为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到零配置的目的。
控制反转是一种 通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IOC容器,其实现方法是依赖注入(Dependency Injection, DI)
HelloSpring案例
导包
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
</dependencies>
如果父工程已经导入过了,则可省略此处。
编写配置文件
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--使用Spring来创建对象,在Spring这些都称为Bean
类型 变量名 = new 类型();
Hello hello = new Hello();
id = 变量名
class = new的对象
property 相当于给对象中的属性设置一个值!
-->
<bean id="hello" class="com.hopeful.pojo.Hello">
<property name="name" value="Hello Spring"/>
</bean>
</beans>
建立实体类
@Data
public class Hello {
private String name;
}
测试
@Test
public void TestStr() {
//获取Spring的上下文对象!
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//我们的对象现在都在Spring中的管理了,我们需要使用,直接去里面取出来就可以!
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello.toString());
}
由上述案例可见,使用对象,没有使用关键字new来创建对象。也就是spring框架对对象进行了管理,即创建,管理和装配。
思考问题
-
对象是由谁创建的?
Hello是由Spring创建的
-
Hello对象的属性是怎么设置的?
Hello对象的属性是由Spring容器设置的。
这个过程就叫控制反转:
控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的
反转:程序本身不创建对象,而变成被动的接收对象
依赖注入:就是利用set方法来进行注入的
IOC是一种编程思想,由主动的编程变成被动的接收。
可以通过newClassPathXmlApplicationContext去浏览一下底层源码。
OK,到现在,我们彻底不中用再程序中去改动了,要实现不同的操作,只需要再xml配置文件中进行修改,所谓的IOC,一句话搞定:对象由Spring来创建,管理,装配!
遇到的问题
applicationContext无法找到getBean方法
解答1: 添加setter和getter方法
解答2: 修改加载的包为 org.springframework.context.ApplicationContext
解答3:手动加载依赖,file -> Project Structure
选择需要的库即可
最后别忘记点击 application或应用,嘻嘻,我的问题就解决了!
Compilation failed: internal java compiler error
编辑版本和jdk版本不一致
可参考文档:https://blog.csdn.net/shizheng_Li/article/details/107050180
org.springframework.beans.factory.config.ConfigurableListableBeanFactory.getApplicationStartup()Lorg…
依赖版本与加载版本不同导致的,我当初用的是spring5.3.9,后来改用5.2.0,导致这样的错误
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3OJEjEdv-1635118396040)(Spring.assets/image-20210917070202185.png)]
调整为自己需要的版本即可!
整合之前的常规写法
-
在原来常规写法的基础上,添加配置文件beans.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="userDaoImpl" class="com.hopeful.dao.UserDaoImpl" /> <bean id="userMySQLImpl" class="com.hopeful.dao.UserMySQLImpl" /> <bean id="userOracleImpl" class="com.hopeful.dao.UserOracleImpl" /> <bean id="userServiceImpl" class="com.hopeful.service.UserServiceImpl"> <!-- ref 引用spring容器中的bean --> <property name="userDao" ref="userOracleImpl"/><!-- 此处只需要修改ref的值,就可以实现调用不同的实现类 --> </bean> </beans>
-
测试
@Test public void testIOC() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); UserServiceImpl userServiceImpl = context.getBean("userServiceImpl", UserServiceImpl.class); userServiceImpl.getUser(); }
Spring创建的方式
- 环境准备
package com.hopeful.pojo;
public class User {
private String name;
public User() {
System.out.println("user通过无参构造函数创建了");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
-
测试
package com.hopeful.pojo; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { public static void main(String[] args) { // 无参函数被调用了 User user = new User(); // user通过无参构造函数创建了 } }
基于 Setter 的依赖项Setter方法
创建beans.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="user" class="com.hopeful.pojo.User"></bean>
</beans>
测试
package com.hopeful.pojo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// spring默认通过无参构造构建
User user = (User)context.getBean("user"); // user通过无参构造函数创建了
}
}
Constructor构建
构造函数参数索引
<bean id="user" class="com.hopeful.pojo.User">
<constructor-arg index="0" value="Hopeful"></constructor-arg>
</bean>
构造函数参数类型匹配,不建议
<bean id="user" class="com.hopeful.pojo.User">
<constructor-arg type="java.lang.String" value="Hopeful2"></constructor-arg>
</bean>
构造函数参数名称,推荐使用
<bean id="user" class="com.hopeful.pojo.User">
<constructor-arg name="name" value="Hopeful3"/>
</bean>
总结:在配置文件加载的时候,容器中管理的对象就已经初始化了! 多次从容器中获取的对象都是同一对象。
Spring配置
alis别名
<alias name="user" alias="userNew"/>
bean的配置
<!-- id:唯一标识符,也就是相当于我们的对象名-->
<!-- class: bean对象的全限定类名,包名 + 类型-->
<!-- name: 也是别名,而且可以起多个别名,用空格/逗号/分号都分割-->
<!-- scopt: 作用域,作用那个范围-->
<bean id="user" class="com.hopeful.pojo.User" name="user2,u2;u3 u4" scope="singleton">
<constructor-arg name="name" value="Hopeful3"/>
</bean>
import
<import resource="beans.xml"/>
<import resource="beans2.xml"/>
<import resource="beans3.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">
<!-- id:唯一标识符,也就是相当于我们的对象名-->
<!-- class: bean对象的全限定类名,包名 + 类型-->
<!-- name: 也是别名,而且可以起多个别名,用空格/逗号/分号都分割-->
<!-- scopt: 作用域,作用那个范围-->
<bean id="user" class="com.hopeful.pojo.User" name="user2,u2;u3 u4" scope="singleton">
<constructor-arg name="name" value="Hopeful3"/>
</bean>
</beans>
依赖注入
构造器注入
前面讲过了
Set方法注入【重点】
-
依赖注入: Set注入
- 依赖: bean的创建需要依赖与容器
- 注入:bean对象中所有属性,由容器注入
<?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="student" class="com.hopeful.pojo.Student" name="user2,u2;u3 u4">--> <!-- <constructor-arg name="name" value="Hopeful3"/>--> <!-- </bean>--> <bean id="address" class="com.hopeful.pojo.Address"></bean> <bean id="student" class="com.hopeful.pojo.Student" name="user2,u2;u3 u4"> <property name="name" value="StudentName"></property> <property name="address" ref="address"></property> <property name="books"> <array> <value>西游记</value> <value>三国演义</value> <value>水浒传</value> <value>红楼梦</value> </array> </property> <property name="hobbys"> <list> <value>篮球</value> <value>羽毛球</value> <value>乒乓球</value> </list> </property> <property name="card"> <map> <entry key="身份证" value="2334342343453453"></entry> <entry key="银行卡" value="23434543545456456"/> </map> </property> <property name="games"> <set> <value>LOL</value> <value>贪吃蛇</value> </set> </property> <!-- null 值--> <property name="wife"> <null/> </property> <property name="info"> <props> <prop key="姓名">张三</prop> <prop key="性别">男</prop> </props> </property> </bean> </beans>
拓展方式注入
官方解释 :
实体类
package com.hopeful.pojo;
public class User {
private String name;
private String pwd;
public User() {
}
public User(String name, String pwd) {
this.name = name;
this.pwd = pwd;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
xml文件,c-namespace和p-namespace
<?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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.hopeful.pojo.User" >
<property name="name" value="Hopeful"/>
<property name="pwd" value="123456"/>
</bean>
<!-- p namespace property注入 相当于上方的-->
<bean id="user2" class="com.hopeful.pojo.User" p:name="Hopeful2" p:pwd="222222"></bean>
<bean id="user3" class="com.hopeful.pojo.User">
<constructor-arg name="name" value="Hopeful3"></constructor-arg>
<constructor-arg name="pwd" value="33333333"/>
</bean>
<!-- c-namespace constructor注入 相当于上方的 -->
<bean id="user4" class="com.hopeful.pojo.User" c:name="Hopeful3" c:pwd="3333333333"/>
</beans>
测试
package com.hopeful;
import com.hopeful.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyUserTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("userBean.xml");
// User users = context.getBean("user1", User.class);
// User users = context.getBean("user2", User.class);
// User users = context.getBean("user3", User.class);
User users = context.getBean("user4", User.class);
System.out.println(users);
}
}
beans作用域
-
单例模式 应用中只有一个实例
<bean id="user" class="com.hopeful.pojo.User" scope="singleton"> <property name="name" value="Hopeful"/> <property name="pwd" value="123456"/> </bean>
public class MyUserTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("userBean.xml"); User users = context.getBean("user", User.class); User users2 = context.getBean("user", User.class); System.out.println(users == users2); // true } }
特点:使用于单线程,并发时使用该模式,容易产生一些延迟
-
原型模式 每次获取创建不同实例
<bean id="user" class="com.hopeful.pojo.User" scope="prototype">
<property name="name" value="Hopeful"/>
<property name="pwd" value="123456"/>
</bean>
public class MyUserTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("userBean.xml");
User users = context.getBean("user", User.class);
User users2 = context.getBean("user", User.class);
System.out.println(users == users2); // false
}
}
特点:比较浪费资源,但多线程不会出现问题
自动装配
-
ByName 通过名称装配
<!-- ByName: 会自动在容器上下文中查找,和自己对象Set方法后面对应的beanId--> <bean id="person" class="com.hopeful.pojo.Person" autowire="byName"> <property name="name" value="Hopeful"/> </bean>
-
ByType 通过类型装配
<!-- ByType: 会自动在容器上下文中查找,和自己对象类型相同的bean--> <bean id="person" class="com.hopeful.pojo.Person" autowire="byType"> <property name="name" value="Hopeful"/> </bean>
ByName 通过名称装配,需保证所有bean的id唯一,该ID名称需要和set方法后面的名称保持一致!
ByType 通过类型装配,需保证所有bean的class唯一,该类型需要在应用中全局唯一!
@Autowired
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 开启使用注解 -->
<context:annotation-config/>
<bean id="dog" class="com.hopeful.pojo.Dog"/>
<bean id="cat" class="com.hopeful.pojo.Cat"/>
<bean id="person" class="com.hopeful.pojo.Person">
<property name="name" value="Hopeful"/>
</bean>
</beans>
实体类
package com.hopeful.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private String name;
@Autowired
private Dog dog;
@Autowired
private Cat cat;
}
测试
package com.hopeful.pojo;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.transaction.annotation.Transactional;
public class MyTest {
@Test
public void testAutowired() {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Person person = context.getBean("person", Person.class);
person.getCat().shut();
person.getDog().shut();
}
}
使用要求:
- 导入约束:context 约束
- 配置注解的支持:
<context:annotation-config/>
注意:
- 直接在属性上使用即可!也可以在set方法上使用
- 使用Autowired,我们可以不用编写Set放高发了,前提是你这个自动装配的属性在IOC容器中,且类型符合ByType或byName
ByName
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<!--使用Spring来创建对象,在Spring这些都称为Bean
类型 变量名 = new 类型();
Hello hello = new Hello();
id = 变量名
class = new的对象
property 相当于给对象中的属性设置一个值!-->
<bean id="dog" class="com.hopeful.pojo.Dog"/>
<bean id="dog2" class="com.hopeful.pojo.Dog"/>
<bean id="person" class="com.hopeful.pojo.Person">
</bean>
</beans>
package com.hopeful.pojo;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import javax.annotation.Resource;
@Data
public class Person {
private String name;
// 通过ByName装配成功
@Autowired
private Animal dog;
}
ByType
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<!--使用Spring来创建对象,在Spring这些都称为Bean
类型 变量名 = new 类型();
Hello hello = new Hello();
id = 变量名
class = new的对象
property 相当于给对象中的属性设置一个值!-->
<bean id="dog1" class="com.hopeful.pojo.Dog"/>
<bean id="person" class="com.hopeful.pojo.Person">
</bean>
</beans>
package com.hopeful.pojo;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import javax.annotation.Resource;
@Data
public class Person {
private String name;
// 通过ByType装配成功
@Autowired
private Animal dog;
}
科普:
// 添加required = false 属性,代表该属性可以为null,否则不能为null(默认为true)
@Autowired(required = false)
// 注解@Autowired源码
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}
如果@Autowired自动装配的环境比较复杂,自动装配无法确认装配具体的bean(存在类型相同的bean),则@Autowired注解就无法单独完成装配,我们可以使用@Qualifier(value=“beanName”)去装配,指定唯一的bean进行注入。
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 开启使用注解 -->
<context:annotation-config/>
<bean id="dog1" class="com.hopeful.pojo.Dog"/>
<bean id="dog2" class="com.hopeful.pojo.Dog"/>
<bean id="cat1" class="com.hopeful.pojo.Cat"/>
<bean id="cat2" class="com.hopeful.pojo.Cat"/>
<bean id="person" class="com.hopeful.pojo.Person">
<property name="name" value="Hopeful"/>
</bean>
</beans>
实体类
package com.hopeful.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private String name;
// 添加required = false 属性,代表该属性可以为null,否则不能为null(默认为true)
@Autowired(required = false)
@Qualifier(value = "dog1")
private Dog dog;
@Autowired
@Qualifier(value = "cat1")
private Cat cat;
}
测试,见上方。
@Resource
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 开启使用注解 -->
<context:annotation-config/>
<bean id="dog1" class="com.hopeful.pojo.Dog"/>
<bean id="dog2" class="com.hopeful.pojo.Dog"/>
<bean id="cat1" class="com.hopeful.pojo.Cat"/>
<bean id="cat2" class="com.hopeful.pojo.Cat"/>
<bean id="person" class="com.hopeful.pojo.Person">
<property name="name" value="Hopeful"/>
</bean>
</beans>
实体类
package com.hopeful.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.Value;
import javax.annotation.Resource;
import javax.annotation.Resources;
/**
* Person
*
* @author : yl
* @version : [v1.0]
* @createTime : [2021/9/24 7:36]
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private String name;
@Resource(name = "dog1")
public Dog dog;
@Resource(name = "cat2")
private Cat cat;
}
测试
package com.hopeful.pojo;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.transaction.annotation.Transactional;
/**
* MyTest
*
* @author : yl
* @version : [v1.0]
* @createTime : [2021/9/24 7:41]
*/
public class MyTest {
@Test
public void testAutowired() {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Person person = context.getBean("person", Person.class);
person.getCat().shut();
person.getDog().shut();
}
}
@Resource 集合了ByName和ByType两方的特性,默认通过ByName查找,如果找不到,再通过ByType查找,如果都找不到,则报错,但可以通过属性name指定特定的bean。
ByName和ByType两方测试可参考@Autowired测试
@Autowired和@Resource异同
总结:
相同:@Autowired和@Resource都是自动装配使用的,都可以在属性上使用
不同:
- @Autowired 通过ByType或ByName 装配bean
- @Resource 先通过ByName装配,找不到,再通过ByType装配,否则报错,但可以通过name属性,指定特定名称的bean
// @Autowired
// @Qualifier(value = "dog1")
@Resource(name = "dog1") // 以上两个注解,相当于下面一个注解,但java注解@Resource没有提示
private Animal dog;
Spring 注解开发
spring4之后,使用注解开发,必须导入aop的包。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iHuYDiev-1635118396047)(Spring.assets/image-20210928070605050.png)]
-
bean的使用
自动装配
指定包扫描
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> <!-- 指定扫描的路径,将其中的类注册到spring容器中 --> <context:component-scan base-package="com.hopeful"/> <!-- <bean id="user" class="com.hopeful.pojo.User">--> <!-- <property name="name" value="HOPEFUL_xml_INNER"/>--> <!-- </bean>--> </beans>
-
属性的注入
package com.hopeful.pojo; import lombok.Data; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Repository; /** * User * * @author : yl * @version : [v1.0] * @createTime : [2021/9/27 20:57] */ @Repository @Data public class User { // 相当于 xml文件中 <property name="name" value="HOPEFUL"/> // @Value("HOPEFUL") private String name; @Value("HOPEFUL22") public void setName(String name) { this.name = name; } }
@value属性的优先级:
xml配置文件位置优先级 > set方法 > 字段属性
-
衍生的注解
@Component有几个衍生注解,我们在web开发中,会按照mvc三层架构分层
- dao 【@Repository】
- service 【@Service】
- controller 【@controller】
这四个注解功能都是一样的,都是代表将该类注解到spring容器中。
-
自动装配
- @Autowired
- @Resource
-
作用域
@Scope(“singleton | prototype”)
package com.hopeful.pojo; import lombok.Data; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Repository; @Repository @Data @Scope("singleton") public class User { // 相当于 xml文件中 <property name="name" value="HOPEFUL"/> @Value("HOPEFUL") private String name; }
[TOC]
-
最佳实践
-
xml与注解
- xml更加万能,适用于任何场景
- 注解不是自己的类使用不了(无法引用其他类型的bean),维护相对复杂。
-
xml与直接的最佳实践
- xml只用来管理bean
- 注解只负责完成属性的注入
- 我们在使用的过程中,只需要注意两点:开启注解的使用,再则扫描指定的包。
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> <!-- 指定扫描的路径,将其中的类注册到spring容器中 --> <context:component-scan base-package="com.hopeful"/> </beans>
-
使用javaConfig实现配置
我们现在要完全不使用Spring的xml配置了,全权交给java来做!
javaConfig是Spring的一个子项目,在Spring4之后,他成为了一个核心功能!
实体类
package com.hopeful.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;
public class User {
@Value("Hopeful_appConfig")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
配置类
package com.hopeful.config;
import com.hopeful.pojo.User;
import org.springframework.context.annotation.*;
// 本质还是一个组件,所以也会交给spring容器托管
// @Configuration 注解,表示该注解是一个配置类,相当于spring中的applicationContext.xml
//导入配置文件
//@ImportResource(locations={"classpath:applicationContext.xml"})
//spring4.2之前导入配置类
//@Import({JavaConfigA.class,JavaConfigB.class})
//spring4.2之后导入普通java bean
//@Import(DemoService.class) // 在spring 4.2之前是不支持的
@Configuration
@ComponentScan("com.hopeful.pojo")
@Import(AppConfig2.class)
@ImportResource(locations={"classpath:applicationContext.xml"})
public class AppConfig {
// 此处无法跳转,只代表idea不支持
// 注册一个bean,就相当于我们之前写的一个bean标签
// 这个方法的名称,就相当于bean标签的id属性
// 这个方法的返回值,就相当于bean标签的class属性
//
@Bean
public User user() {
return new User();
}
}
测试
package com.hopeful;
import com.hopeful.config.AppConfig;
import com.hopeful.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MyTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
User user = context.getBean("user", User.class);
System.out.println(user.getName());
}
}
之前使用xml配置文件,通过ClassPathXmlApplicationContext
类来获取IOC容器
现在javaConfig配置,通过AnnotationConfigApplicationContext
类来获取IOC容器
代理模式
为什么要学习代理模式?因为这就是SpringAOP的底层!【SpringAOP + SpringMVC】
代理分类:
- 静态代理
- 动态代理
租房为例
房东租房:
中介出租房子
静态代理
角色分析:
- 抽象角色:一般会使用接口或抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真实角色后,我们一般会进行附属操作
- 客户:访问代理对象的人
接口
package com.hopeful.demo1;
public interface Rent {
public void rent();
}
真实角色
package com.hopeful.demo1;
// 房东
public class Landlord implements Rent {
public void rent() {
System.out.println("房东要租赁房子。。。");
}
}
代理角色
package com.hopeful.demo1;
// 中介
public class Proxy implements Rent {
private Landlord landlord;
public Proxy(Landlord landlord) {
this.landlord = landlord;
}
public Proxy() {
}
public void rent() {
lookHouse();
landlord.rent();
signAContract();
}
// 看房子
public void lookHouse() {
System.out.println("中介带人看房~");
}
// 签合同
public void signAContract(){
System.out.println("中介和我们签合同");
}
}
测试
package com.hopeful.demo1;
public class Client {
public static void main(String[] args) {
// 直接找房东出租房子
// Landlord landlord = new Landlord();
// landlord.rent();
// 通过中介租房
Landlord landlord = new Landlord(); // 房东租赁房子
// 代理:中介帮房东租房子,但是代理一般会有一些附属操作
Proxy proxy = new Proxy(landlord);
// 直接找中介租房,不用找房东即可
proxy.rent();
}
}
代理模式的好处:
- 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
- 公共也就交给代理角色!实现业务的分工
- 公共业务发生扩展的时候,方便集中管理!
缺点:
- 一个真实的角色就会产生一个代理角色 ;代码会翻倍——开发效率就会变低!
代理加深理解
添加日志模块
接口
package com.hopeful.demo2;
public interface IUserService {
public void add();
public void delete();
public void update();
public void query();
}
真实角色
package com.hopeful.demo2;
/**
* UserDao
*
* @author : yl
* @version : [v1.0]
* @createTime : [2021/9/28 22:08]
*/
public class UserService implements IUserService {
public void add() {
System.out.println("新增了一个用户");
}
public void delete() {
System.out.println("删除 了一个用户");
}
public void update() {
System.out.println("更新了一个用户");
}
public void query() {
System.out.println("查询了一个用户");
}
// 在公司中改动业务代码,是大忌
}
代理对象
package com.hopeful.demo2;
/**
* Proxy
*
* @author : yl
* @version : [v1.0]
* @createTime : [2021/9/28 22:16]
*/
public class Proxy implements IUserService {
private IUserService userService;
public void setUserService(IUserService userService) {
this.userService = userService;
}
public void add() {
log("add()");
userService.add();
}
public void delete() {
log("delete()");
userService.delete();
}
public void update() {
log("update()");
userService.update();
}
public void query() {
log("query()");
userService.query();
}
public void log(String method) {
System.out.println("【debug】 执行了" + method +"方法");
}
}
用户
package com.hopeful.demo2;
public class Client {
public static void main(String[] args) {
// UserService userService = new UserService();
// userService.add();
Proxy proxy = new Proxy();
proxy.setUserService(new UserService());
proxy.add();
}
}
动态代理
代理模式的好处:
-
可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
-
公共也就交给代理角色!实现业务的分工
-
公共业务发生扩展的时候,方便集中管理!
-
动态代理和静态代理角色一样
-
动态代理的代理类是动态生成的,不是我们直接写好的
-
动态代理分为两类:基于接口的动态代理,基于类的动态代理
- 基于接口-jdk动态代理【我们在这里使用】
- 基于类:cglib
- java字节码实现:javasist
-
需要了解两个类:Proxy代理,invocationHandler 调用处理程序
IUserService接口
package com.hopeful.demo2;
public interfajavace IUserService {
public void add();
public void delete();
public void update();
public void query();
}
UserService实现类
package com.hopeful.demo2;
/**
* UserDao
*
* @author : yl
* @version : [v1.0]
* @createTime : [2021/9/28 22:08]
*/
public class UserService implements IUserService {
public void add() {
System.out.println("新增了一个用户");
}
public void delete() {
System.out.println("删除 了一个用户");
}
public void update() {
System.out.println("更新了一个用户");
}
public void query() {
System.out.println("查询了一个用户");
}
// 在公司中改动业务代码,是大忌
}
代理处理程序
package com.hopeful.demo4;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyInvocationHandler implements InvocationHandler {
private Object target;
public void setTarget(Object target) {
this.target = target;
}
// 生成被代理的对象
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
// 处理代理实例,并返回结果
// 代理对象调接口中的方法---代理对象的真身是$proxy0 调用了对应的方法---此方法内部调用其父类的成员h调用h的invoke方法---就是调用传入了InvocationHandler的invoke方法,至于返回值,那就看我们的InvocationHandler的实现类怎么写了。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
Object object = method.invoke(target,args);
return object;
}
public void log(String msg) {
System.out.println("执行了"+ msg +"方法");
}
}
测试
package com.hopeful.demo4;
import com.hopeful.demo2.IUserService;
import com.hopeful.demo2.UserService;
/**
* Client
*
* @author : yl
* @version : [v1.0]
* @createTime : [2021/10/12 7:19]
*/
public class Client {
public static void main(String[] args) {
// 真实角色
UserService userService = new UserService();
// 代理角色 没有
//代理调用处理程序
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setTarget(userService);
// 获取代理类
// 代理对象调接口中的方法---代理对象的真身是$proxy0 调用了对应的方法---此方法内部调用其父类的成员h调用h的invoke方法---就是调用传入了InvocationHandler的invoke方法,至于返回值,那就看我们的InvocationHandler的实现类怎么写了。
IUserService proxy = (IUserService) pih.getProxy();
proxy.delete();
}
}
代理模式的好处:
- 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
- 公共也就交给代理角色!实现业务的分工
- 公共业务发生扩展的时候,方便集中管理!
- 动态代理类代理的是一个接口,一般就是对应的一类业务
- 一个动态代理类可以代理多个类,只要是实现了同一个接口即可。
聊聊AOP
AOP
AOP的Spring原生实现-使用Spring API 接口
1.引入依赖:
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
2.编写接口与实现类
package com.hopefu.service;
public interface IUserService {
public void add();
public void delete();
public void update();
public void select();
}
package com.hopefu.service;
public class UserServiceImpl implements IUserService {
@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 select() {
System.out.println("执行了select方法");
}
}
3.编写日志
package com.hopefu.log;
import org.springframework.aop.AfterAdvice;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class AfterLog implements AfterReturningAdvice {
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("执行了"+method.getName()+"方法,返回值是"+o);
}
}
package com.hopefu.log;
import org.springframework.aop.AfterAdvice;
import org.springframework.aop.BeforeAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import javax.sound.midi.Soundbank;
import java.lang.reflect.Method;
public class BeforeLog implements MethodBeforeAdvice {
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("执行了" + method.getName() + "方法前打印日志");
}
}
4.编写配置文件
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="com.hopefu.service.UserServiceImpl"></bean>
<bean id="afterLog" class="com.hopefu.log.AfterLog"></bean>
<bean id="beforeLog" class="com.hopefu.log.BeforeLog"></bean>
<aop:config>
<!-- 切入点:expression:表达式,execution(* * * * *)-->
<aop:pointcut id="pointcut" expression="execution(* com.hopefu.service.UserServiceImpl.*(..))"/>
<!-- 环绕通知增强-->
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
测试
package com.hopeful.service;
import com.hopefu.service.IUserService;
import com.hopefu.service.UserServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* MyTest
*
* @author : yl
* @version : [v1.0]
* @createTime : [2021/10/13 21:06]
*/
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
IUserService userService = context.getBean("userService", IUserService.class);
userService.add();
}
}
自定义AOP
package com.hopeful.diy;
public class PoinCutDiy {
public void before() {
System.out.println("before");
}
public void after() {
System.out.println("after");
}
}
编写接口IUserService与实现类UserServiceImpl参考使用SpringAPI接口的案例
配置文件
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 方式二 -->
<bean id="userService" class="com.hopeful.service.UserServiceImpl"/>
<bean id="pointCutDiy" class="com.hopeful.diy.PoinCutDiy"/>
<aop:config>
<aop:aspect ref="pointCutDiy">
<aop:pointcut id="point" expression="execution(* com.hopeful.service.UserServiceImpl.*(..))"/>
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
</beans>
测试:参考使用SpringAPI接口的案例
Spring-AOP中Pointcut中Expression表达式的学习可参考:Spring-AOP中Pointcut中Expression表达式
使用注解
自定义切面
package com.hopeful.diy;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect // 标记切面
public class AnnotationPointDiy {
// 前置通知
@Before("execution(* com.hopeful.service.UserServiceImpl.*(..))")
public void before() {
System.out.println("执行方法前==========");
}
// 后置通知
@After("execution(* com.hopeful.service.UserServiceImpl.*(..))")
public void after() {
System.out.println("执行方法后===========");
}
// 环绕通知
@Around("execution(* com.hopeful.service.UserServiceImpl.*(..))")
// pj 连接点 可以获取执行方法的一些方法
public void around(ProceedingJoinPoint pj) throws Throwable {
System.out.println("环绕通知前=========");
// 通过连接点调用执行的方法
Object proceed = pj.proceed();
System.out.println("环绕通知后=========");
}
}
编写接口IUserService与实现类UserServiceImpl参考使用SpringAPI接口的案例
配置文件
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注册bean-->
<bean id="annotationPointDiy" class="com.hopeful.diy.AnnotationPointDiy"/>
<bean id="userService" class="com.hopeful.service.UserServiceImpl"/>
<!-- 开启注解支持-->
<aop:aspectj-autoproxy/>
</beans>
测试:参考使用SpringAPI接口的案例
AOP在Spring中的作用
提供声明式事务:允许用户自定义切面
- 横切关注点:跨越应用程序多个模块的方法或功能,即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点,如日志,安全,缓存,事务等
- 切面(ASPECT,aspect):横切关注点被模块化的特殊对象,即他就是一个类
- 通知(Advice):切面必须要完成的工作,即它是类中的一个方法。
- 目标(Target):被通知对象
- 代理(Proxy):向目标对象应用通知之后创建的对象
- 切入点(Pointcut):切面通知执行的“地点”的定义。
- 连接点(JointPoint):与切入点匹配的执行点
方式一:使用Spring 的API接口
方法 二:自定义AOP
方法三:使用注解
整合Mybatis和Spring
回顾Mybatis
- 添加依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.10</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.10</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
</dependencies>
总结:我们平常最大的scope疑问在provided 和 runtime之间。记住一个原则就行,如果容器有的jar,在项目pom.xml中就使用provided; 如果需要动态加载的jar就使用runtime 。默认scope是compile
-
创建实体类
package com.hopeful.pojo; import lombok.Data; @Data public class User { private int id; private String name; private String pwd; }
-
编写核心配置文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <typeAliases> <package name="com.hopeful.pojo"/> </typeAliases> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/hopeful/mapper/UserMapper.xml"/> </mappers> </configuration>
-
编写接口
package com.hopeful.dao; import com.hopeful.pojo.User; import java.util.List; public interface UserMapper { List<User> selectUser(); }
-
编写mapper.xml文件
<?xml version="1.0" encoding="UTF8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.hopeful.dao.UserMapper"> <select id="selectUser" resultType="user"> select * from user; </select> </mapper>
-
测试
package com.hopeful.test; import com.hopeful.dao.UserMapper; import com.hopeful.pojo.User; import org.apache.ibatis.io.Resources; import org.apache.ibatis.ognl.security.UserMethod; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.util.List; public class MyTest { @Test public void selectUsers() throws IOException { String resource = "mybatisConfig.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> usersList = mapper.selectUser(); for (User user : usersList) { System.out.println(user); } } }
整合一
-
配置数据源
-
配置SqlSessionFactory
-
配置SqlSessionTemplate
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- dataSource:使用spring中JDBC数据源 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean> <!-- SqlSessionFactory --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <!-- 绑定mybatis配置文件 --> <property name="configLocation" value="classpath:mybatisConfig.xml"/> <property name="mapperLocations" value="classpath:com/hopeful/mapper/*.xml"/> </bean> <!-- sqlSessionTemplate:就是我们使用的sqlSession --> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <!-- <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>--> <constructor-arg index="0" ref="sqlSessionFactory"/> </bean> <!-- 将接口实现类注册到spring容器中 --> <bean id="userMapper" class="com.hopeful.dao.UserMapperImpl"> <property name="sqlSession" ref="sqlSession"/> </bean> </beans>
-
编写接口Mapper实现类
package com.hopeful.dao; import com.hopeful.pojo.User; import org.mybatis.spring.SqlSessionTemplate; import java.util.List; public class UserMapperImpl implements UserMapper { private SqlSessionTemplate sqlSession; public void setSqlSession(SqlSessionTemplate sqlSession) { this.sqlSession = sqlSession; } public List<User> selectUser() { UserMapper mapper = sqlSession.getMapper(UserMapper.class); return mapper.selectUser(); } }
-
测试
package com.hopeful.test; import com.hopeful.dao.UserMapper; import com.hopeful.pojo.User; import org.apache.ibatis.io.Resources; import org.apache.ibatis.ognl.security.UserMethod; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import javax.jws.soap.SOAPBinding; import java.io.IOException; import java.io.InputStream; import java.util.List; public class MyTest { @Test public void selectUsers() throws IOException { ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml"); UserMapper userMapper = context.getBean("userMapper", UserMapper.class); List<User> users = userMapper.selectUser(); for (User user : users) { System.out.println(user); } } }
方式二:使用SqlSessionDaoSupport
编写 实现类
package com.hopeful.dao;
import com.hopeful.pojo.User;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import java.util.List;
// SqlSessionDaoSupport 提供SqlSessionTemplate
// 继承sqlSessionDaoSupport可以通过getSqlSession获取sqlSession
public class UserMapperImpl3 extends SqlSessionDaoSupport implements UserMapper {
public List<User> selectUser() {
// 调用 getSqlSession() 方法你会得到一个 SqlSessionTemplate
return getSqlSession().getMapper(UserMapper.class).selectUser();
}
}
测试
package com.hopeful.test;
import com.hopeful.dao.UserMapper;
import com.hopeful.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.ognl.security.UserMethod;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.jws.soap.SOAPBinding;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class MyTest {
@Test
public void selectUsers() throws IOException {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
UserMapper userMapper = context.getBean("userMapper3", UserMapper.class);
List<User> users = userMapper.selectUser();
for (User user : users) {
System.out.println(user);
}
}
}
方式二:
通过MapperFactoryBean
将接口加入到 Spring 中
<bean id="userMapper2" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.hopeful.dao.UserMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
实现类
package com.hopeful.dao;
import com.hopeful.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import java.util.List;
public class UserMapperImpl2 implements UserMapper {
private UserMapper userMapper;
public List<User> selectUser() {
return userMapper.selectUser();
}
}
测试
@Test
public void selectUsers() throws IOException {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
UserMapper userMapper = context.getBean("userMapper2", UserMapper.class);
List<User> users = userMapper.selectUser();
for (User user : users) {
System.out.println(user);
}
}
遇到的问题
2字节的UTF-8序列的字节2无效
使用标签时,也出现这种情况。
这个问题是因为在xml文件
中的中文注释的问题,有两种解决方案:
一、将xml文件
的中文注释全部删除,解决
二、将xml文件
顶部的encoding=UTF-8
改为encoding=UTF8
可以解决问题
特殊技能
想随意调节B站倍速播放 ,谷歌浏览器按f12,点选console
输入 document.querySelector(“video”).playbackRate = 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/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--使用Spring来创建对象,在Spring这些都称为Bean
类型 变量名 = new 类型();
Hello hello = new Hello();
id = 变量名
class = new的对象
property 相当于给对象中的属性设置一个值!
-->
<bean id="hello" class="com.hopeful.pojo.Hello">
<property name="name" value="Hello Spring"/>
</bean>
</beans>
自动装配配置
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 开启使用注解 -->
<context:annotation-config/>
<bean id="dog" class="com.hopeful.pojo.Dog"/>
<bean id="cat" class="com.hopeful.pojo.Cat"/>
<bean id="person" class="com.hopeful.pojo.Person">
<property name="name" value="Hopeful"/>
</bean>
</beans>
Spring注解
@Autowired
注意:
- 直接在属性上使用即可!也可以在set方法上使用
- 使用Autowired,我们可以不用编写Set放高发了,前提是你这个自动装配的属性在IOC容器中,且类型符合ByName或ByType
@Resource
该注解集成了ByName和ByType属性,先通过ByName查找,然后通过ByType查找,找不到报错,但可以通过name属性指定特定名称的bean。
相同:@Autowired和@Resource都是自动装配使用的,都可以在属性上使用
不同:
- @Autowired 通过ByType 或ByName 装配bean
- @Resource 先通过ByName装配,找不到,再通过ByType装配,否则报错,但可以通过name属性,指定特定名称的bean
@Nullable
从 Spring Framework 5.0 开始,您还可以使用@Nullable
注解
@Nullable:表示特定参数,返回值或字段可以是null
的 注解。
@Component
使用该注解,说明该类被Spring容器管理了。
@ComponentScan
@ComponentScan(“packageName”) 告诉Spring容器哪个包下带有注解的类被扫描,并自动装入bean容器!
注:@Configuration注解申明当前类是一个配置类,相当于xml配置文件。@ComponentScan和@Configuration一起使用的原因就是基于Spring2.0中的注解配合xml配置文件的实现一样,即在xml配置文件配置ComponentScan包扫描属性。
如果类中用了@Controller,@Repository,@Service,@Component四大注解标识之一了,那么如果不加上@ComponentScan,Spring就不会自动扫描类上的四大注解中的任何一个,那么四大注解下的类就不会被Spring扫描到,更不会装入Spring容器中,因此你配置的四大注解就失去了作用。