概述
Maven引入
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.3</version>
</dependency>
HelloSpring
实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User {
private Integer id;
private String username;
private String password;
}
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就是java对象 , 由Spring创建和管理-->
<!--id为获取唯一标识-->
<bean id="user" class="com.demo.entity.User">
<property name="id" value="1"/>
<property name="username" value="admin"/>
<property name="password" value="admin"/>
</bean>
</beans>
测试
@Test
public void hello() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// User user = context.getBean("user", User.class);
User user = (User) context.getBean("user");
System.out.println(user);
}
创建对象的方式
在配置文件加载的时候。其中管理的对象都已经初始化了
set注入
创建对象:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
private String name;
private Teacher teacher;
private String[] books;
private List<String> hobbies;
private Map<String, String> cards;
private Set<String> games;
private String wife;
private Properties infos;
}
常量注入
<bean id="teacher" class="com.demo.entity.Teacher">
<property name="name" value="李老师"/>
</bean>
bean注入
<bean id="student" class="com.demo.entity.Student">
<property name="name" value="张三"/>
<!--引用类型用ref,引用Spring容器里的id-->
<property name="teacher" ref="teacher"/>
</bean>
数组注入
<bean id="student" class="com.demo.entity.Student">
<property name="name" value="张三"/>
<property name="teacher" ref="teacher"/>
<property name="books">
<array>
<value>西游记</value>
<value>红楼梦</value>
<value>水浒传</value>
</array>
</property>
</bean>
List注入
<property name="hobbies">
<list>
<value>听歌</value>
<value>看电影</value>
<value>爬山</value>
</list>
</property>
Map注入
<property name="cards">
<map>
<entry key="工商银行" value="123"/>
<entry key="农业银行" value="456"/>
</map>
</property>
Set注入
<property name="games">
<set>
<value>魔兽世界</value>
<value>星际争霸</value>
<value>英雄联盟</value>
</set>
</property>
Null注入
<property name="wife"><null/></property>
Properties注入
<property name="infos">
<props>
<prop key="age">14</prop>
<prop key="gender">男</prop>
<prop key="address">北京</prop>
</props>
</property>
构造器注入
<!--构造器index下标-->
<bean id="user1" class="com.demo.entity.User">
<!--从0开始-->
<constructor-arg index="0" value="1"/>
<constructor-arg index="1" value="admin1"/>
<constructor-arg index="2" value="123"/>
</bean>
<!--构造器参数名字-->
<bean id="user2" class="com.demo.entity.User">
<constructor-arg name="id" value="2"/>
<constructor-arg name="username" value="admin2"/>
<constructor-arg name="password" value="123"/>
</bean>
<!--构造器参数类型-->
<bean id="user3" class="com.demo.entity.User">
<constructor-arg type="java.lang.Integer" value="3"/>
<!--参数类型相同时按顺序赋值-->
<constructor-arg type="java.lang.String" value="admin3"/>
<constructor-arg type="java.lang.String" value="123"/>
</bean>
扩展注入
P命名空间注入
导入约束 : xmlns:p="http://www.springframework.org/schema/p"
<!--P(属性: properties)命名空间 , 属性依然要设置set方法-->
<bean id="user" class="com.kuang.pojo.User" p:name="张三" p:age="18"/>
C命名空间注入
导入约束 : xmlns:c="http://www.springframework.org/schema/c"
<!--C(构造: Constructor)命名空间 , 属性依然要设置set方法-->
<bean id="user" class="com.kuang.pojo.User" c:name="张三" c:age="18"/>
Spring配置
别名
<alias name="user" alias="userAlias"/>
Bean的配置
别名
<!--id唯一标识符,name别名-->
<bean id="user" name="u1 u2,u3;u4" class="com.demo.entity.User">
<property name="id" value="1"/>
<property name="username" value="admin"/>
<property name="password" value="admin"/>
</bean>
作用域
<bean id="user" class="xxx.xx" scope="作用域">
<property name="id" value="1"/>
</bean>
import
导入其他Spring配置文件,合并为一个配置文件
<import resource="bean1.xml"/>
<import resource="bean2.xml"/>
Bean的自动装配
- 自动装配是使用spring满足bean依赖的一种方法
- spring会在应用上下文中为某个bean寻找其依赖的bean
Spring中bean有三种装配机制,分别是:
- 在xml中显式配置;
- 在java中显式配置;
- 隐式的bean发现机制和自动装配。
byName
<bean id="teacher" class="com.demo.entity.Teacher">
<property name="name" value="李老师"/>
</bean>
<bean id="stu" class="com.demo.entity.Student" autowire="byName">
<property name="name" value="张三"/>
</bean>
当一个bean节点带有 autowire byName 的属性时
- 将查找其类中所有的set方法名,例如setCat,获得将set去掉并且首字母小写的字符串,即cat。
- 去spring容器中寻找是否有此字符串名称id的对象。
- 如果有,就取出注入;如果没有,就报空指针异常。
byType
<bean class="com.demo.entity.Teacher">
<property name="name" value="李老师"/>
</bean>
<bean id="stu" class="com.demo.entity.Student" autowire="byType">
<property name="name" value="张三"/>
</bean>
使用autowire byType首先需要保证:同一类型的对象,在spring容器中唯一。如果不唯一,会报不唯一的异常。
使用注解
准备工作:
- 在spring配置文件中引入context文件头
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
- 开启属性注解支持
<context:annotation-config/>
@Autowired
- @Autowired是按类型自动装配的,不支持id匹配
- 有多个同类型时尝试按set id byName匹配,没有则报错
@Autowired(required = false) // required = false 表示对象可以为null
private Teacher teacher;
@Qualifier
- @Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配
- @Qualifier不能单独使用
@Autowired()
@Qualifier("teacher1") // 指定id
private Teacher teacher;
@Resource
- @Resource如有指定的name属性,先按该属性进行byName方式查找装配;
- 其次再进行默认的byName方式进行装配;
- 如果以上都不成功,则按byType的方式自动装配;
- 都不成功(多个同类型),则报异常。
@Resource(name = "teacher1")
private Teacher teacher;
使用注解开发
在spring4之后,想要使用注解形式,必须得要引入aop的包
引入context约束:
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
开启包扫描
<!--开启注解支持-->
<context:annotation-config/>
<!--开启包扫描,让指定包下的注解生效,由IOC容器统一管理-->
<context:component-scan base-package="com.demo.entity"/>
bean注入
@Component
@Component("teacher1") // 等价于 <bean id="teacher1" class="com.demo.entity.Teacher"/>,默认id为类名首字母小写
public class Teacher {
private String name;
}
@Repository
dao层
@Controller
web层
@Service
service层
属性注入
@Value
@Value("李老师")
private String name;
@Autowired
@Scope作用域
@Component
@Scope("prototype")
public class Teacher {
}
基于Java类进行配置
可以省去配置文件
新建类
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
// 不自动纳入容器
public class Dog {
private String name;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Component // 自动纳入Spring容器
public class Teacher {
@Value("李老师")
private String name;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Component // 自动纳入Spring容器
public class Student {
@Value("张三")
private String name;
@Resource
private Teacher teacher;
}
新建配置类
@Configuration // 代表这是一个配置类
@ComponentScan("com.demo.entity") // 扫描其他包下的自动Bean
@Import(MyConfig.class) //导入合并其他配置类,类似于配置文件中的 import 标签
public class AppConfig {
@Bean// 通过方法手动注册一个bean,这里的返回值就Bean的类型,方法名就是bean的id
public Dog dog() {
return Dog.builder().name("旺财").build();
}
@Bean("dog1")// 也可以自定义bean名
public Dog dog() {
return Dog.builder().name("旺财1").build();
}
}
测试
@Test
public void test() {
// 从配置类获取上下文环境
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Dog dog = context.getBean("dog", Dog.class);
System.out.println(dog);
Teacher teacher = context.getBean("teacher", Teacher.class);
System.out.println(teacher);
Student student = context.getBean("student", Student.class);
System.out.println(student);
}
代理模式
静态代理
租房案例
Rent接口:
public interface Rent {
/**
* 出租房子
*/
void rent();
}
房东:
public class Host implements Rent{
/**
* 出租房子
*/
@Override
public void rent() {
System.out.println("房东出租房子");
}
}
中介:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Proxy implements Rent {
private Host host;
/**
* 出租房子
*/
@Override
public void rent() {
seeHouse();
host.rent();
}
public void seeHouse() {
System.out.println("中介带你看房");
}
}
客户:
public class Client {
public static void main(String[] args) {
Host host = new Host();
// host.rent();
Proxy proxy = new Proxy(host);
proxy.rent();
}
}
新增日志案例
UserService:
public interface UserService {
void getUser();
void addUser();
}
UserServiceImpl:
public class UserServiceImpl implements UserService{
@Override
public void getUser() {
System.out.println("获取用户");
}
@Override
public void addUser() {
System.out.println("新增用户");
}
}
UserServiceProxy:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserServiceProxy {
private UserService userService;
public void getUser() {
log("使用了getUser方法");
userService.getUser();
}
public void addUser() {
log("使用了addUser方法");
userService.addUser();
}
public void log(String msg) {
System.out.println(msg);
}
}
Client:
public class Client {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
// userService.getUser();
// userService.addUser();
UserServiceProxy userServiceProxy = new UserServiceProxy(userService);
userServiceProxy.getUser();
userServiceProxy.addUser();
}
}
动态代理
- 动态代理的角色和静态代理的一样 .
- 动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的
- 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理
- 基于接口的动态代理----JDK动态代理
- 基于类的动态代理--cglib
- 现在用的比较多的是 javasist 来生成动态代理 .
JDK的动态代理需要了解两个类
- 核心:
InvocationHandler
和Proxy
核心:一个动态代理 , 一般代理某一类业务 , 一个动态代理可以代理多个类,代理的是接口!
处理类
public class ProxyInvocationHandler implements InvocationHandler {
// 被代理的接口
private Object target;
public ProxyInvocationHandler(Object target) {
this.target = target;
}
// 生成得到代理类
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
// 处理代理实例,并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("中介带你看房");
Object result = method.invoke(target, args);
System.out.println("签订合同");
return result;
}
}
动态生成代理对象
public class Client2 {
public static void main(String[] args) {
// 真实角色
Host host = new Host();
// 设置要代理的对象
ProxyInvocationHandler pih = new ProxyInvocationHandler(host);
// 生成代理角色
Rent rent = (Rent) pih.getProxy();
rent.rent();
}
}
AOP
需要导入一个包
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
通过 Spring API 实现
AOP的约束
xmlns:aop="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
新建Active类
public class BeforeLog implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName() + "的" + method.getName() + "执行前");
}
}
public class AfterLog implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了" + method.getName() + ",返回结果为:" + returnValue);
}
}
配置文件配置
<!--注册bean-->
<bean id="userService" class="com.demo.service.UserServiceImpl"/>
<bean id="beforeLog" class="com.demo.log.BeforeLog"/>
<bean id="afterLog" class="com.demo.log.AfterLog"/>
<!--配置aop-->
<!--方式一:使用原生Spring API接口-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pointcut" expression="execution(* com.demo.service.UserServiceImpl.*(..))"/>
<!--配置环绕-->
<aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
测试
public class ClientAop {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 动态代理代理的是接口
UserService userService = context.getBean("userService", UserService.class);
userService.getUser();
userService.addUser();
}
}
自定义类
新建切面类
public class MyAspect {
public void before() {
System.out.println("===方法执行前===");
}
public void after() {
System.out.println("===方法执行后===");
}
}
配置文件配置
<!--方式二:自定义切面-->
<!--引入自定义切面类-->
<bean id="myAspect" class="com.demo.advice.MyAspect"/>
<aop:config>
<!--切面配置-->
<aop:aspect ref="myAspect">
<!--切入点配置-->
<aop:pointcut id="pointcut" expression="execution(* com.demo.service.UserServiceImpl.*(..))"/>
<!--环绕配置-->
<aop:before method="before" pointcut-ref="pointcut"/>
<aop:after method="after" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
测试
使用注解实现
新建切面类
@Aspect
public class AnnotationAspect {
@Before("execution(* com.demo.service.UserServiceImpl.*(..))")
public void before() {
System.out.println("===AnnotationAspect执行前===");
}
@After("execution(* com.demo.service.UserServiceImpl.*(..))")
public void after() {
System.out.println("===AnnotationAspect执行后===");
}
@Around("execution(* com.demo.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
System.out.println(jp.getSignature());
// 执行方法
Object proceed = jp.proceed();
System.out.println(proceed);
System.out.println("环绕后");
}
}
配置文件配置
<!--方式三:注解-->
<!--引入自定义切面类-->
<bean id="annotationAspect" class="com.demo.aspect.AnnotationAspect"/>
<!--开启注解支持-->
<aop:aspectj-autoproxy/>
测试
整合Mybatis
Maven引入
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--MyBatis相关-->
<dependency><!--数据库驱动-->
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<dependency><!--mybatis-->
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency><!--mybatis-spring整合-->
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<!--MyBatis相关-->
<!--spring相关-->
<dependency><!--Spring核心-->
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.3</version>
</dependency>
<dependency><!--事务-->
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.3</version>
</dependency>
<dependency><!--AOP-->
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
<!--spring相关-->
</dependencies>
<build>
<!-- 防止资源导出错误 -->
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
mybatis-config.xml
<?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>
<settings>
<!--开启日志-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!--开启下划线转驼峰-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
<!--别名配置-->
<typeAliases>
<package name="com.demo.entity"/>
</typeAliases>
<!--Mapper注册-->
<mappers>
<package name="com.demo.mapper"/>
</mappers>
</configuration>
appcationContext.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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--加载Properties配置文件-->
<context:property-placeholder location="classpath:db.properties"/>
<!--DataSource-->
<!--使用Spring的数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</bean>
<!--SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!--绑定mybatis配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<!--以下两个配置需要再增加一个实现类-->
<!--sqlSession-->
<!--<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">-->
<!-- <constructor-arg ref="sqlSessionFactory"/>-->
<!--</bean>-->
<!--注册userMapperImpl-->
<!--<bean id="userMapper" class="com.demo.mapper.UserMapperImpl">-->
<!-- <property name="sqlSession" ref="sqlSession"/>-->
<!--</bean>-->
<!--不需要增加实现类-->
<!--MapperFactoryBean 将会负责 SqlSession 的创建和关闭-->
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.demo.mapper.UserMapper"/>
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
</beans>
*新增加的实现类:
public class UserMapperImpl implements UserMapper{
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
/**
* 获取全部用户
*/
@Override
public List<User> getUserList() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.getUserList();
}
}
测试
@Test
public void testGetUserList() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
List<User> userList = userMapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
}
声明式事务
新增事务约束
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
配置事务管理器
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
配置事务环绕
<!--配置事务环绕-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--配置哪些方法使用什么样的事务,配置事务的传播特性-->
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="update" propagation="REQUIRED"/>
<tx:method name="search*" propagation="REQUIRED"/>
<tx:method name="get" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
配置aop
<!--配置aop织入事务-->
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.demo.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
测试
public class UserServiceImpl {
private UserMapper userMapper;
public void setUserMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}
public void test() {
userMapper.addUser(new User(10, "root10", "root10"));
int i = 10/0;
userMapper.deleteUser(10);
}
}
<bean id="userService" class="com.demo.service.UserServiceImpl">
<property name="userMapper" ref="userMapper"/>
</bean>
@Test
public void testTransaction() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserServiceImpl userService = context.getBean("userService", UserServiceImpl.class);
userService.test();
}
结果:出错事务回滚,没有插入进去