发表于: 2019-10-26 21:33:37
2 969
啥也不说就是干!!!
今天完成的事情:
1、DI(依赖注入)
什么是依赖:依赖是指 Bean 实例中的属性
属性分为:简单类型(8种基本类型和 String 类型)、POJO 类型、集合数组类型
什么是依赖注入
Dependency Injection依赖注入是 Spring 核心框架 IoC 的具体实现
依赖注入的方式(基于XML)
1) 构造函数注入
<bean id="student" class="com.gerry.jnshu.bean.Student">
<constructor-arg value="1" index="0"></constructor-arg>
<!--如果字面值包含特殊字符可以使用![CDATA[]]包裹起来:比如 name 字面值为 <zhouzihui>-->
<!--属性值也可以使用value子节点进行配置-->
<constructor-arg>
<value><![CDATA[<zhouzihui>]]></value>
</constructor-arg>
</bean>
涉及的标签:constructor-arg
index:指定参数在构造函数参数列表的索引位置
name:指定参数在构造函数中的名称
value:它能赋的值是基本数据类型和 String 类型
ref:它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件中配置过的 bean
2) set 方法注入
手动装配方式(XML 方式):通过bean标签的子标签property来完成,需要在类中指定setter方法。
a. 简单类型(value):
<bean id="student" class="com.gerry.jnshu.bean.Student">
<property name="name" value="张三"></property>
<property name="school" value="河南师范"></property>
<property name="qq" value="12345678"></property>
</bean>
b. 引用类型(ref)
public class Student {
private int id;
private Address address;
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;}
//省略部分源码
}
对于 Student 中有个属性为引用类型 Address:
public class Address {
private String province;
private String city;
private String distinct;
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;}
//省略部分源码
}
那么如果Student 想要注入 Address 属性,则需要使用 <ref> 标签,指向 Address bean
<bean id="student" class="com.gerry.jnshu.bean.Student">
<property name="name" value="张三"></property>
<property name="school" value="河南师范"></property>
<property name="qq" value="12345678"></property>
<property name="address" ref="address"></property>
</bean>
<bean id="address" class="com.gerry.jnshu.bean.Address">
<property name="province" value="河南"></property>
</bean>
c. 集合类型
如果是数组或者 List 集合,注入配置文件的方式是一样的:
<bean id="collectionBean" class="com.gerry.jnshu.CollectionBean">
<property name="arrs">
<list>
//如果集合内是简单类型,使用value子标签,如果是POJO类型,则使用bean标签
<value>美美</value>
<value>小风</value>
<bean></bean>
</list>
</property>
</bean>
如果是Set集合,注入的配置文件方式如下:
<property name="sets">
<set>
//如果集合内是简单类型,使用value子标签,如果是POJO类型,则使用bean标签
<value>哈哈</value>
<value>呵呵</value>
</set>
</property>
如果是Map集合,注入的配置方式如下:
<property name="map">
<map>
<entry key="老王2" value="38"/>
<entry key="凤姐" value="38"/>
<entry key="如花" value="29"/>
</map>
</property>
如果是Properties集合的方式,注入的配置如下:
<property name="pro">
<props>
<prop key="uname">root</prop>
<prop key="pass">123</prop>
</props>
</property>
Spring 基于注解和 XML 混合方式使用:
学习基于注解的 IoC 配置,大家脑海里首先得有一个认知,即注解配置和 xml 配置要实现的功能都是一样的,都是要降低程序间的耦合。只是配置的形式不一样。
关于实际的开发中到底使用xml 还是注解,不同的场景有着不同的使用习惯。所以这两种配置方式我们都需要掌握。
这里要把 spring 的 xml 配置内容改为使用注解逐步实现。
IoC 注解使用方法
1)spring配置文件中,配置context:component-scan标签
<!--开启注解扫描指定包中带有注解的类-->
<context:component-scan base-package="com.gerry.jnshu"/>
2)类上面加上注解 @Component 或者它的衍生注解 @Controller @Service @Repository
IoC 注解(创建对象)相当于 <bean id="" class="">
1) @Component注解
作用:
把资源让 spring 来管理。相当于在 xml 中配置一个 bean。
属性:
value:指定 bean 的 id。
如果不指定 value 属性,默认 bean 的 id 是当前类的类名。首字母小写。
2)@Controller、@Service、@Repository注解
这三个注解都是针对@Component的衍生注解
他们的作用及属性都是一模一样的。他们只不过是提供了更加明确的语义化。
@Controller:一般用于表现层的注解。
@Service:一般用于业务层的注解。
@Repository:一般用于持久层的注解。
细节:如果注解中有且只有一个属性要赋值时,且名称是 value,value 在赋值是可以不写。
DI注解(依赖注入)相当于:<property name="" ref="">
1)@Autowired
@Autowired默认按类型装配(byType)
@Autowired是由AutowiredAnnotationBeanPostProcessor类实现
@Autowired是spring自带的注解
@Autowired默认情况下要求依赖对象必须存在,如果需要允许 null 值,可以设置它的 required 属性为 false,如:@Autowired(required=false)
如果我们想按名称装配(byName)可以结合 @Qualifier 注解进行使用
2) @Qualifier
在自动按照类型注入的基础之上,再按照 Bean 的 id 注入。
它在给字段注入时不能独立使用,必须和@Autowire 一起使用;
但是给方法参数注入时,可以独立使用。
3)@Resource
@Resource默认按名称装配(byName),可以通过@Resource的name属性指定名称
@Resource属于J2EE JSR250规范的实现
@Resource如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,当找不到与名称匹配的bean时才按照类型进行装配。
但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
推荐使用@Resource注解,因为这个注解是属于J2EE的,减少了与spring的耦合。这样代码看起就比较优雅。
关于注解和 XML 的选择问题
注解的优势:配置简单,维护方便(我们找到类,就相当于找到了对应的配置)
XML 的优势:修改时,不用改源码,不涉及重新编译和部署
基于注解的 IoC 配置已经完成,到此发现依然离不开 spring 的 XML 配置文件,那么能不能不写 application.xml 所有配置都用注解来实现呢?
Spring 基于纯注解的使用:
当我们需要摆脱 xml 配置文件时候,思考一下:
注解扫描配置(怎么去掉)
<!--开启注解扫描指定包中带有注解的类-->
<context:component-scan base-package="com.gerry.jnshu"/>
非自定义 Bean 配置(如:SqlSessionFactory 和 BasicDataSource 配置)
<!-- 配置sqlSessionFactory,SqlSessionFactoryBean是用来产生sqlSessionFactory的 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 加载数据源,使用上面配置好的数据源 -->
<property name="dataSource" ref="dataSource"/>
</bean>
之前的创建方式为通过读取 XML 文件进行创建,去掉 XML 后,该如何创建 ApplicationContext 呢?
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
@Configuration
从 Spring3.0 开始可以使用 @Configuration 定义配置类,代替 xml 配置文件
相当于 xml 文件中的 <beans> 根标签
配置类内部包含一个或多个 @Bean 注解的方法,这些方法将会被 AnnotationConfigApplicationContext(AnnotationConfigWebApplicationContext)类进行扫描
并用于构建 bean 定义对象,初始化 Spring 容器
比如:
@Configuration
public class SpringConfiguration{
public SpringConfiguration(){
System.out.println("容器启动初始化。。。");
}
}
@Bean
@Bean 标注在方法上(返回讴歌实例的方法),等价于 Spring 配置文件中的 <bean>
用来注册 bean 对象(主要用来配置非自定义的 bean,如 DruidDataSource、SqlSessionFactory)
包含的属性有:
1)name:给当前 @Bean 注解方法创建的对象指定一个名称(即 bean 的 id)如果不指定,默认与标注的方法名相同
2)@Bean 注解默认作用域为单例 singleton 作用域,可通过 @Scope("prototype")指定为原型作用域
比如:
@Configuration
public class SpringConfiguration{
public SpringConfiguration(){
System.out.println("容器启动初始化。。。");
}
@Bean
@Scope("prototype")
public StudentService studentService() {
return new StudentServiceImpl();
}
}
@ComponentScan
相当于 context:component-scan 标签
组件扫描器,扫描 @Component @Controller、@Service、@Repository 注解的类,该注解是编写在类上面,一般配合 @Configuration 注解一起使用
包含的属性有:
1)basePackages:用于指定要扫描的包
2)value:和 basePackages 作用一样
比如:
@Service
public class StudentServiceImpl implements StudentService{
@Overide
public void saveStudent(){
System.out.println("保存学生 Service 实现");
}
}
@Configuration
public class SpringConfiguration{
public SpringConfiguration(){
System.out.println("容器启动初始化。。。");
}
@Bean
@Scope("prototype")
public StudentService studentService(){
return new StudentServiceImpl();
}
}
@PropertySource
相当于 context:property-placeholder 标签 加载 Properties 配置文件,编写在类上面
包含的属性有:
1)value[] 用于指定 properties 文件路径,如果在类路径下,需要写上 classpath
实例代码:
@Configuratio
@PropertySource("classpath:jdbc.properties")
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
/**
* 创建一个数据源,并存入 spring 容器中
*
* @return
*/
@Bean(name = "dataSource")
public DataSource createDataSource() {
try {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setDriverClass(driver);
ds.setJdbcUrl(url);
ds.setUser(username);
ds.setPassword(password);
return ds;
}catch (Exception e) {
throw new RuntimeException(e);
}
}
}
properties 文件:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///spring
jdbc.username=root
jdbc.password=root
@Import
相当于 Spring 配置文件中 import 标签,用来组合多个配置类
包含的属性:
value:用来指定其他配置文件类
@Configuration
@ComponentScan(basePackages = "com.gerry.jnshu")
@Import({ JdbcConfig.class })
public class SpringConfiguration {
}
@Configuration
@PropertySource("classpath:jdbc.properties")
public class JdbcConfig {
}
纯注解方式创建上下文容器
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
StudentService studentService = context.getBean(StudentService.class);
studentService.insertInfo();
2、Spring AOP
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程
AOP采取横向抽取机制,补充了传统纵向继承体系(OOP)无法解决的重复性代码优化(性能监视、事务管理、安全检查、缓存),将业务逻辑和系统处理的代码(关闭连接、事务管理、操作日志记录)解耦
Spring 基于 AspectJ 的 AOP 使用
Spirng 将 AspectJ 收录到自身的框架中,并且底层依然采用的是动态代理的方式
1)添加 AspectJ 的 AOP 依赖
<!-- 基于AspectJ的aop依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
2)编写目标类和目标方法
配置目标类,将目标类交给 Spring IoC 容器管理
<context:component-scan base-package="com.gerry.jnshu" />
a. 使用 XML 实现 Spring AOP
编写通知(增强类,一个普通的类)
public class MyAdvice {
public void log(){
System.out.println("记录日志...");
}
}
配置通知,将通知类交给 Spring IoC 容器管理
<!--配置通知、增强-->
<bean name="myAdvice" class="com.gerry.jnshu.service.MyAdvice"/>
配置 AOP 切面
<!--AOP配置-->
<aop:config>
<aop:aspect ref="myAdvice">
<aop:pointcut id="pointcut" expression="execution(void com.gerry.jnshu.service.impl.StudentServiceImpl.insertInfo())"/>
<aop:before method="log" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
关于切入点表达式:excution([修饰符] 返回值类型 包名.类名.方法名(参数))
修饰符:可忽略
返回值类型:必须有,但可以使用 * 通配符
包名:多级包名用 . 分割,包名可以使用 * 代替,多级包名可以使用多个 * 代替,如果中间想要省略包名可以使用 ..
类名:可以使用 * 代替,也可以写成 *ServiceImpl
方法名:也可以使用 * 代替,也可以写成 add*
参数:可以使用 * 代替,如果多个参数,可以使用 .. 代替
Student student = new Student();
student.setName("zhangsan");
StudentService studentService = (StudentService) ctx.getBean("studentService");
studentService.insertInfo(student);
通知类型:前置通知、后置通知、最终通知、环绕通知、异常抛出通知
1)前置通知:目标对象方法之前执行
<aop:before method="before" pointcut-ref="pointcut"/>
用于方法开始前进行校验
2) 后置通知:目标对象方法之后执行
<aop:after-returning method="afterReturning" pointcut-ref="pointcut"/>
用于方法之后处理
3)返回通知
<aop:after method="before" pointcut-ref="pointcut"/>
4) 最终通知:目标对象方法之后执行,有没有异常都会执行
<aop:after-return method="afterReturn" pointcut-ref="pointcut"/>
用于释放资源等
5)环绕通知:目标对象方法之前和之后都会执行
<aop:around method="around" pointcut-ref="pointcut"/>
用于事务、统计代码执行时机
6)异常抛出通知:在抛出异常后通知
<aop:after-throwing method=" afterThrowing " pointcut- ref="pointcut"/>
包装异常
b.使用 XML +注解 实现 Spring AOP
编写切面类(该类中还指定了切入点)
@Component("myAdvice")
@Aspect
public class MyAdvice {
@Before(value="execution(* *..*.*ServiceImpl.*(..))")
public void log(){
System.out.println("记录日志...");
}
}
配置切面类,交给 Spring 容器
<context:component-scan base-package="com.gerry.jnshu"/>
开启 AOP 自动代理
<aop:aspectj-autoproxy />
这里记录个知识点:point 还可以单独在切面类中定义出来
@Pointcut(value = "execution(* *..*.*ServiceImpl.*(..))")
public void log2(){
System.out.println("统一配置切入点...");
}
@Before("MyAdvice.log2()")
public void beforeMethod(){
System.out.println("开始方法");
}
c.基于纯注解 Spring AOP 配置方式:@EnableAspectJAutoProxy
接下来的计划:
今天就学到这里,接下来学习 Spring 项目整合部分
遇到的问题:
暂无
收获:
掌握了Spring IoC 基本知识,大致了解了 AOP 的使用
评论