一、重点IOC AOP
(1)、IOC
- 什么是IOC
- IOC是控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。
- 在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。
- 主要是set方法。后期用注解可以不用生成set方法
- 是到xml中实现对象的创建。
(2)、AOP
- 什么是AOP
- 面向切片编程
二、初次尝试案例
1、准备
- 需要准备全局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"> <!--这里面完成对对象的注入 理解为容器--> </beans>
- 当是团队合作时,会有xml冲突的问题,spring也解决了
<import resource="{path}/beans.xml"/>
- 准备几个类去测试
- 类准备好了之后需要放到xml文件中去
<!--这是最普通的类 没有含参构造--> <!--id相当于new出来的对象名 class指类型 需要全名指定--> <bean id="" class=""> <property name="name" value="value"/> . . . </bean>
- 准备测试类
public class Mytest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("配置文件名"); Teacher teacher = context.getBean("在xml文件里的对象id",类名.class); System.out.println(teacher.toString()); } }
三、现在正式学习
1、使用Bean创建对象
- 含参构造的对象
<!--方法一 使用下标--> <bean id="" class="" > <constructor-arg index="index" value="value" /> </bean> <!--方法二 使用参数名--> <bean id="" class="" > <constructor-arg name="parameter" value="value" /> </bean> <!--方法三 使用属性类型处理 不建议 -->
- 在使用Bean注入的时候可以使用别名
<!--设置别名:在获取Bean的时候可以使用别名获取--> <alias name="userT" alias="userNew"/> <!--bean就是java对象,由Spring创建和管理--> <!-- id 是bean的标识符,要唯一,如果没有配置id,name就是默认标识符 如果配置id,又配置了name,那么name是别名 name可以设置多个别名,可以用逗号,分号,空格隔开 如果不配置id和name,可以根据applicationContext.getBean(.class)获取对象; class是bean的全限定名=包名+类名 --> <bean id="hello" name="hello2 h2,h3;h4" class="com.kuang.pojo.Hello"> <property name="name" value="Spring"/> </bean>
- 当被创建的对象里有复杂属性
<bean id="OutClassID" class=""> <property name="" value="" /> </bean> <bean id="InnerClassID" class="" > <!--普通属性--> <property name="" value="" /> <!--引用外部类属性--> <property name="InnerParameter" ref="OutClassID" /> <!--数组属性--> <property name="" > <array> <value>语文book</value> <value>英语book</value> <value>数学book</value> </array> </property> <!--list属性--> <property name=""> <list> <value>china</value> <value>english</value> <value>math</value> </list> </property> <!--map属性--> <property name=""> <map> <entry key="zhuli" value="zyp"/> <entry key="zhuli1" value="zyp1"/> <entry key="zhuli2" value="zyp2"/> </map> </property> <!--set属性--> <property name=""> <set> <value>篮球</value> <value>篮球1</value> <value>篮球2</value> </set> </property> <!--properties属性--> <property name=""> <props> <prop key="name">朱溧</prop> <prop key="sex">男</prop> </props> </property> <!--空指针或者空值--> <!--<property name="nullPoint" value="" />--> <property name="nullPoint"> <null/> </property> </bean>
- Bean的拓展注入
- 有两个(p、c)
- 使用需要导入约束
xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c"
<!--P(属性: properties)命名空间 , 属性依然要设置set方法--> <bean id="" class="" p:properties1="" p:properties2=""/> <!--C(构造: Constructor)命名空间 , 属性依然要设置set方法--> <bean id="" class="" c:attribute1="" c:attribute2=""/>
- Bean的作用域
- 作用域介绍
作用域有多: request、session作用域仅在基于web的应用中使用(不必关心你所采用的是什么web应用框架), 只能用在基于web的Spring ApplicationContext环境。
- Singleton
当一个bean的作用域为Singleton,那么Spring IoC容器中只会存在一个共享的bean实例, 并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。 Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用, 他都存在了,每次获取到的对象都是同一个对象。 注意,Singleton作用域是Spring中的缺省作用域。 要在XML中将bean定义成singleton,可以这样配置:
<bean id="" class="" scope="singleton">
- Prototype
当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。 Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。 Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。 根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。 在XML中将bean定义成prototype,可以这样配置:
<bean id="account" class="com.foo.DefaultAccount" scope="prototype"/>
- 作用域介绍
2、Bean自动装配
有三种装配方法
在xml中显式配置;
在java中显式配置;
隐式的bean发现机制和自动装配。
- xml显式配置
其实就是在xml文件里进行bean的创建配置
- Java显式配置后面会讲到
- autowire byName (按名称自动装配)
<!--有引入类 本类引用了 使用autowire 原理就是自动到本类下的set方法下找对应的变量名去匹配xml里的然后自动装配 全局id唯一 --> <bean id="dog" class="com.kuang.pojo.Dog"/> <bean id="cat" class="com.kuang.pojo.Cat"/> <bean id="user" class="com.kuang.pojo.User" autowire="byName"> <property name="str" value="qinjiang"/> </bean>
- autowire byType (按类型自动装配)
<!--有引入类 本类引用了 使用autowire 原理就是自动到本类下的set方法下找对应的类去匹配xml里的然后自动装配 使用autowire byType首先需要保证:同一类型的对象,在spring容器中唯一。如果不唯一,会报不唯一的异常。 全局class唯一 --> <bean id="dog" class="com.kuang.pojo.Dog"/> <bean id="cat" class="com.kuang.pojo.Cat"/> <bean id="user" class="com.kuang.pojo.User" autowire="byType"> <property name="str" value="qinjiang"/> </bean>
1. 使用注解开发
1. @Autowired
```xml
<!--需要引入约束和开启注解-->
<context:annotation-config/>
```
```xml
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
```
```java
public class User {
/* 搭配required可以使得属性为空
@Autowired(required = false)
private Cat cat;
或者在set方法里 传参时加入@nullable
public Cat setCat(@nullable Cat cat) {
this.cat = cat;
}
* */
@Autowired
private Cat cat;
@Autowired
private Dog dog;
private String str;
public Cat getCat() {
return cat;
}
public Dog getDog() {
return dog;
}
public String getStr() {
return str;
}
}
```
```xml
<context:annotation-config/>
<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat" class="com.kuang.pojo.Cat"/>
<bean id="user" class="com.kuang.pojo.User"/>
```
2. @Qualifier 不可以单独使用 搭配 @Autowired
```text
由于@Autowired是使用type去扫描的 如果说出现多个相同类的bean
则需要使用@Qualifier去指定id
```
```java
@Autowired
@Qualifier(value = "cat2")
private Cat cat;
@Autowired
@Qualifier(value = "dog2")
private Dog dog;
```
3. @Resource
```text
@Resource如有指定的name属性,
先按该属性进行byName方式查找装配;
其次再进行默认的byName方式进行装配;
如果以上都不成功,则按byType的方式自动装配。
都不成功,则报异常。
```
```java
public class User {
//如果允许对象为null,设置required = false,默认为true
@Resource(name = "cat2")
private Cat cat;
@Resource
private Dog dog;
private String str;
}
```
4. 使用Java配置类
```text
完全使用Java进行开发 运用到注解、配置类
配置类就相当于配置文件
只能使用AnnotationConfigApplicationContext上下文获取容器
通过加载配置类的class文件
```
演示:
pojo
```java
@Component//表示把整个类放入容器进行托管
public class User {
private String name;
public User() {
}
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Value("zhuli")
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
```
ConfigClass:
```java
@Configuration//表示这是一个配置类 等价于xml文件 本身也被spring托管
@ComponentScan("com.zhuli.pojo")//扫描包
@Import(UserConfig2.class)
public class UserConfig {
/*
* 注册一个Bean
* 方法名就是xml里的id 方法返回类型就是xml里的class return的对象就是要注入的Bean对象
* */
@Bean
public User getUser(){
return new User();
}
}
```
AOP介绍引入
理解顺序:静态代理、动态代理、AOP
静态代理
- 解释
类似中介,A租房子,但是不去找房东B(B出租),而是交给中介公司C(C出租)去代理筹备,都进行同一件事(将事件变成接口)
A和B都去实现接口,但是怎么体现代理的效果?就是利用set或构造方法
这会产生以下类:客户类、房东类、中介类、接口类(出租)
- 代码展示
//接口
public interface Rent {
public void rent();
}
//房东出租房子
public class Host implements Rent{
@Override
public void rent() {
System.out.println("出租房子");
}
}
//重点!!!代理类也实现接口 并且把Host类拿进来代理Host类(通过构造方法)
//作为代理类还可以去进行其他操作,意味着横向扩展业务
public class Proxy implements Rent{
private Host host;
public Proxy(Host host) {
this.host = host;
}
@Override
public void rent() {
host.rent();
otheruse();
}
public void otheruse(){
System.out.println("签合同");
}
}
//客户类只需要拿到房东类对象然后放进中介类对象里然后访问中介类就行了
public class Client {
public static void main(String[] args) {
Host host = new Host();
Proxy proxy = new Proxy(host);
proxy.rent();
}
}
但是这里会有一个缺陷,那就是一个业务产生就会产生一个代理类
代码翻倍增长,下面就涉及动态代理
动态代理入门
即动态生成代理类 涉及一个类InvocationHandler
可以把动态生成代理类抽象成一个工具类
//接口类
public class Client {
public static void main(String[] args) {
//获取真实角色
UserImpl user = new UserImpl();
//此时没有代理对象
ProxyUtils proxyUtils = new ProxyUtils();
//设置要代理的对象
proxyUtils.setTarget(user);
//动态生成代理类 注意 由于返回的是接口 只能向上转接口类型
UserMapper proxy = (UserMapper)proxyUtils.getProxy();
proxy.cre();
}
}
//工具类
public class ProxyUtils implements InvocationHandler {
//被代理的接口
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//生成代理类
public Object getProxy() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
}
//处理代理实列 并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
getOthers(method.getName());
Object invoke = method.invoke(target, args);
return invoke;
}
//扩充业务
public void getOthers(String msg){
System.out.println("使用了"+msg+"方法");
}
}
//实现类
public class UserImpl implements UserMapper{
@Override
public void cre() {
System.out.println("增加了用户");
}
@Override
public void sele() {
System.out.println("选择了用户");
}
}
//这样就可以通过工具类生成一个代理类去做操作
AOP入门
但是在spring里就不需要再去写工具类了,它以及帮我们处理好了
需要导包和写定约束 有三种方法
<!--织入包-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<!--约束-->
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
<!--基本配置-->
<bean id="afterlog" class="com.zhuli.log.AfterLog" />
<bean id="beforelog" class="com.zhuli.log.BeforeLog" />
<bean id="userserviceimpl" class="com.zhuli.service.UserServiceImpl" />
<bean id="diy" class="com.zhuli.diy.Diy" />
<bean id="useanno" class="com.zhuli.anno.UseAnno" />
方式一:使用原生spring api接口
<aop:config><!--开启aop配置-->
<!--(id)切入点=id (expression)表达式="execution(指定位置 * * * * *)"-->
<aop:pointcut id="pointcutname" expression="execution(* com.zhuli.service.UserServiceImpl.*(..))"/>
<!--(advice-ref)切入的类 (pointcut-ref)切入点-->
<aop:advisor advice-ref="afterlog" pointcut-ref="pointcutname" />
<aop:advisor advice-ref="beforelog" pointcut-ref="pointcutname" />
</aop:config>
<!--方法二 使用自定义实现(切面定义)-->
<aop:config>
<!--ref指定作为切面类-->
<aop:aspect ref="diy" >
<!--指定切入点以及书写表达式去指定类-->
<aop:pointcut id="pointcutname" expression="execution(* com.zhuli.service.UserServiceImpl.*(..))"/>
<!--根据切面类里的方法去进行增加操作-->
<aop:before method="atBefore" pointcut-ref="pointcutname"/>
<aop:after method="atAfter" pointcut-ref="pointcutname"/>
</aop:aspect>
</aop:config>
<!--第三种 使用注解开发-->
<!--需要开启aop注解-->
<aop:aspectj-autoproxy/>
<!--
指定切面类通过注解@Aspect标注
方法执行前或后都在切面类里的方法前使用对应注解
-->
整合Mybatis
<!--以下是所需的配置-->
dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<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>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.9.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.7</version>
</dependency>
</dependencies>
使用比较的方法引导spring接管mybatis
<!--下面这些都是固定的-->
<!--配置spring的数据源 代替mybatis的加载数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/zf02"/>
<property name="username" value="root" />
<property name="password" value="iamsorry36" />
</bean>
<!--还需要注入SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" >
<!--绑定数据源-->
<property name="dataSource" ref="dataSource" />
<!--绑定mybatis配置文件这一步最好怼上-->
<property name="configLocation" value="classpath:mybatis_config.xml" />
<!--下面就是和在mybatis里一样注册接口对应的xml文件-->
<property name="mapperLocations" value="com/zhuli/Mapper/StreetMapper.xml" />
</bean>
<!--但是有一个问题,原先的sqlsession被spring里的sqlsessiontemplate接替了,
而sqlsessiontemplate是由sqlsessionfactory产生,
这里就有另一种方法替我们解决,使用sqlsessiondaosupport,可以减少sqlsessiontemplate的注入
但是实现类需要继承sqlsessiondaosupport
-->
<!--注入sqlsessiontemplate与sqlsession相同 使用了spring的jdk
有了这个就可以拿到sqlsessiontemplate
-->
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate" >
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
<!--如果不写上面这个bean那就继承sqlsessiondaosupport-->
<bean id="smi2" class="com.zhuli.Mapper.StreetMapperImpl2" >
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
ion被spring里的sqlsessiontemplate接替了,
而sqlsessiontemplate是由sqlsessionfactory产生,
这里就有另一种方法替我们解决,使用sqlsessiondaosupport,可以减少sqlsessiontemplate的注入
但是实现类需要继承sqlsessiondaosupport
–>
```xml
<!--注入sqlsessiontemplate与sqlsession相同 使用了spring的jdk
有了这个就可以拿到sqlsessiontemplate
-->
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate" >
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
<!--如果不写上面这个bean那就继承sqlsessiondaosupport-->
<bean id="smi2" class="com.zhuli.Mapper.StreetMapperImpl2" >
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
Spring事务
这里处理一下spring对于事务的解决办法
分
声明式事务 AOP
编程式事务 在原有代码中改变
约束
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
<!--开启事务-->
<tx:jta-transaction-manager />
织入事务的步骤:
1、配置事务管理器
2、配置事务通知 把事务管理器仍进去 广播默认required
这个时候已经拿到接口里的所有方法,那么就是需要把哪些方法添加到事务中
3、进行AOP配置 切入点 切入bean