依赖:Spring Web MVC
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.2.3.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.2.3.RELEASEversion>
dependency>
BeanFactory
,它是工厂模式的实现。BeanFactory
使用控制反转(IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。 控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC。
Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。
Hello实体类:
public class Hello {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("Hello,"+ name );
}
}
编写spring文件,bean.xml
<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="hello" class="com.kuang.pojo.Hello">
<property name="name" value="Spring"/>
bean>
beans>
测试:
@Test
public void test(){
//解析beans.xml文件 , 生成管理相应的Bean对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//getBean : 参数即为spring配置文件中bean的id .
Hello hello = (Hello) context.getBean("hello");
hello.show();
}
控制反转:将对象的创建者由程序转换为Spring,程序本身不创建对象,编程被动的接收对象。
依赖注入:本质上采用set方法进行注入,若将实体类中的setter方法注释,bean中会报错
我们实现不同的操作 , 只需在xml配置文件中进行修改 , 不需修改程序,所谓的IoC就是对象由Spring 来创建 , 管理 , 装配 !
bean.xml
<bean id="user" class="com.xyzhang.pojo.User">
<property name="name" value="xyzhang"/>
bean>
实体类:
public class UserT {
private String name;
public UserT(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("name="+ name );
}
}
bean.xml:
<bean id="userT" class="com.xyzhang.pojo.UserT">
<constructor-arg index="0" value="xyzhang"/>
bean>
<bean id="userT" class="com.xyzhang.pojo.UserT">
<constructor-arg name="name" value="xyzhang"/>
bean>
<bean id="userT" class="com.xyzhang.pojo.UserT">
<constructor-arg type="java.lang.String" value="xyzhang"/>
bean>
在配置文件加载时,容器中管理的对象就会进行初始化,无论后续是否使用。
设置别名后 在获取Bean时可以使用别名获取(bean中的name可实现相同效果)
<bean id="hello" name="hello2 h2,h3;h4" class="com.xyzhang.pojo.Hello">
<property name="name" value="Spring"/>
bean>
可以将多个配置文件导入合并为一个(适用于团队合作)
要求被注入的属性 , 必须要有setter
实体类:
Address.java
public class Address {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
Student.java
public class Student {
private String name;
private Address address;
private String[] books;
private List<String> hobbys;
private Map<String,String> card;
private Set<String> games;
private String wife;
private Properties info;
public void setName(String name) {
this.name = name;
}
public void setAddress(Address address) {
this.address = address;
}
public void setBooks(String[] books) {
this.books = books;
}
public void setHobbys(List<String> hobbys) {
this.hobbys = hobbys;
}
public void setCard(Map<String, String> card) {
this.card = card;
}
public void setGames(Set<String> games) {
this.games = games;
}
public void setWife(String wife) {
this.wife = wife;
}
public void setInfo(Properties info) {
this.info = info;
}
public void show(){
System.out.println("name="+ name
+ ",address="+ address.getAddress()
+ ",books="
);
for (String book:books){
System.out.print("<<"+book+">>\t");
}
System.out.println("\n爱好:"+hobbys);
System.out.println("card:"+card);
System.out.println("games:"+games);
System.out.println("wife:"+wife);
System.out.println("info:"+info);
}
}
<bean id="student" class="com.xyzhang.pojo.Student">
<property name="name" value="小明"/>
bean>
先在容器中创建出引用类型,通过ref在需要处引用
<bean id="addr" class="com.xyzhang.pojo.Address">
<property name="address" value="西大直街92号"/>
bean>
<bean id="student" class="com.xyzhang.pojo.Student">
<property name="name" value="小明"/>
<property name="address" ref="addr"/>
bean>
<property name="books">
<array>
<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="456456456465456"/>
<entry key="建设" value="1456682255511"/>
map>
property>
<property name="games">
<set>
<value>LOLvalue>
<value>BOBvalue>
<value>COCvalue>
set>
property>
<property name="info">
<props>
<prop key="学号">20190604prop>
<prop key="性别">男prop>
<prop key="姓名">小明prop>
props>
property>
以下两种不能直接使用,需要导入xml头文件
P命名空间注入(不需使用构造器,默认无参构造器即可):需要在头文件中加入约束条件
导入约束 : xmlns:p="http://www.springframework.org/schema/p"
<bean id="user" class="com.xyzhang.pojo.User" p:name="xyzhang" p:age="18"/>
C命名空间注入(使用实体类中的有参构造器注入):需要在头文件中加入约束条件
导入约束 : xmlns:c="http://www.springframework.org/schema/c"
<bean id="user" class="com.xyzhang.pojo.User" c:name="xyzhang" c:age="18"/>
后四种作用域仅在基于web的应用中使用(不必关心你所采用的是什么web应用框架),只能用在基于web的Spring ApplicationContext环境。
Singleton单例模式(Spring默认模式)
Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。
Prototype原型模式
Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。
Request
Session
application
byName 按名称自动装配
修改bean的配置增加属性autowire=“byName”
<bean id="user" class="com.xyzhang.pojo.User" autowire="byName">
<property name="str" value="xyzhang"/>
bean>
当bean节点设置该属性后
byType 按类型自动装配
使用前需要保证,同一type的对象在spring容器中唯一,否则会报异常。
<bean id="user" class="com.xyzhang.pojo.User" autowire="byType">
<property name="str" value="xyzhang"/>
bean>
<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
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
beans>
可直接在属性上使用,也可在set方法上直接使用!
使用@Autowired注解可以不用编写setter方法,前提是自动装配的属性在IoC容器中存在,且符合名字byName!
实体类:
public class User {
@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;
}
}
配置文件bean.xml
<bean id="dog" class="com.xyzhang.pojo.Dog"/>
<bean id="cat" class="com.xyzhang.pojo.Cat"/>
<bean id="user" class="com.xyzhang.pojo.User"/>
其中:@Autowired(required=false) required=false表示对象可以为null;true,对象不能为null。默认为true
使用方法:
实体类:
@Autowired
@Qualifier(value = "cat1")
private Cat cat;
@Autowired
@Qualifier(value = "dog1")
private Dog dog;
bean.xml
<bean id="dog1" class="com.kuang.pojo.Dog"/>
<bean id="cat1" class="com.kuang.pojo.Cat"/>
@Resource与@Autowired区别:
配置扫描哪些包下的注解
<context:component-scan base-package="com.xyzhang.pojo"/>
在指定包下编写类,添加注解
@Component("user")
// 相当于配置文件中
public class User {
public String name = "xyzhang";
}
测试
@Test
public void test(){
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("beans.xml");
User user = (User) applicationContext.getBean("user");
System.out.println(user.name);
}
可以不使用setter方法,直接在属性名上添加 @Value(‘xxx’)
@Component("user")
// 相当于配置文件中
public class User {
@Value("xyzhang")
// 相当于配置文件中
public String name;
}
若提供了setter方法,可以直接在set方法上添加@Value(‘xxx’)
@Component("user")
public class User {
public String name;
@Value("xyzhang")
public void setName(String name) {
this.name = name;
}
}
@Component三个衍生注解,在web开发中按照mvc三层架构分层,这四个注解功能完全相同。
@Autowired
@Qualifier
@Resource
作用域中可选singleton,prototype…
//该例相当于实现了整个Bean的功能
@Controller("user")
@Scope("prototype")
public class User {
@Value("xyzhang")
public String name;
}
XML与注解比较
xml与注解整合开发:【最佳实践】
<context:component-scan base-package="com.xyzhang.pojo"/>
<context:annotation-config/>
实体类:
@Component //将这个类标注为Spring的一个组件,放到容器中!
public class Dog {
public String name = "dog";
}
新建config配置文件
//代表这是一个配置类 该类也会被注册到容器中 因为本身就是一个@Component
//相当于bean.xml
@Configuration
public class MyConfig {
@Bean //通过方法注册一个bean,这里的返回值就Bean的类型,方法名就是bean的id!返回值相当于bean中的class
public Dog dog(){
//返回要注入到bean的对象
return new Dog();
}
}
测试类
@Test
public void test2(){
ApplicationContext applicationContext =
new AnnotationConfigApplicationContext(MyConfig.class);
Dog dog = (Dog) applicationContext.getBean("dog");
System.out.println(dog.name);
}
角色分析:
静态代理举例:“中介租房”
//抽象角色:租房
public interface Rent {
public void rent();
}
//真实角色: 房东,房东要出租房子
public class Host implements Rent{
public void rent() {
System.out.println("房屋出租");
}
}
//代理角色:中介
public class Proxy implements Rent {
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
//租房 代理角色一般会有一些附属操作!
public void rent(){
seeHouse();
host.rent();
fare();
}
//看房
public void seeHouse(){
System.out.println("带房客看房");
}
//收中介费
public void fare(){
System.out.println("收中介费");
}
}
//客户类,一般客户都会去找代理!
public class Client {
public static void main(String[] args) {
//房东要租房
Host host = new Host();
//中介帮助房东
Proxy proxy = new Proxy(host);
//你去找中介!
proxy.rent();
}
}
静态代理的好处:
缺点:
JDK的动态代理需要了解两个类:
核心 : InvocationHandler 调用处理程序, Proxy 代理
代码实现:
Rent.java 抽象角色
//抽象角色:租房
public interface Rent {
public void rent();
}
Host.java
//真实角色: 房东,房东要出租房子
public class Host implements Rent{
public void rent() {
System.out.println("房屋出租");
}
}
ProxyInvocationHandler.java
public class ProxyInvocationHandler implements InvocationHandler {
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
//生成代理类,重点是第二个参数,获取要代理的抽象角色!之前都是一个角色,现在可以代理一类角色
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
rent.getClass().getInterfaces(),this);
}
// proxy : 代理类 method : 代理类的调用处理程序的方法对象.
// 处理代理实例上的方法调用并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse();
//核心:本质利用反射实现!
Object result = method.invoke(rent, args);
fare();
return result;
}
//看房
public void seeHouse(){
System.out.println("带房客看房");
}
//收中介费
public void fare(){
System.out.println("收中介费");
}
}
Client.java
//租客
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//代理实例的调用处理程序
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setRent(host); //将真实角色放置进去!
Rent proxy = (Rent)pih.getProxy(); //动态生成对应的代理类!
proxy.rent();
}
}
核心:一个动态代理 , 一般代理某一类业务 , 一个动态代理可以代理多个类,代理的是接口!
动态代理的好处:
使用AOP织入,需要导入依赖包
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.4version>
dependency>
通过SpringAPI实现【SpringAPI接口实现】
public interface UserService {
public void add();
public void delete();
public void update();
public void search();
}
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("增加用户");
}
@Override
public void delete() {
System.out.println("删除用户");
}
@Override
public void update() {
System.out.println("更新用户");
}
@Override
public void search() {
System.out.println("查询用户");
}
}
public class Log implements MethodBeforeAdvice {
//method : 要执行的目标对象的方法
//objects : 被调用的方法的参数
//Object : 目标对象
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println( o.getClass().getName() + "的" + method.getName() + "方法被执行了");
}
}
public class AfterLog implements AfterReturningAdvice {
//returnValue 返回值
//method被调用的方法
//args 被调用的方法的对象的参数
//target 被调用的目标对象
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了" + target.getClass().getName()
+"的"+method.getName()+"方法,"
+"返回值:"+returnValue);
}
}
<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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="com.xyzhang.service.UserServiceImpl"/>
<bean id="log" class="com.xyzhang.log.Log"/>
<bean id="afterLog" class="com.xyzhang.log.AfterLog"/>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.xyzhang.service.UserServiceImpl.*(..))"/>
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
aop:config>
beans>
pointcut切入点表达式含义:
execution (* com.sample.service.impl…*.* (…))包含五个部分:
1、execution(): 表达式主体。
2、第一个 * 号:表示返回类型,* 号表示所有的类型。
3、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。
4、第二个 * 号:表示类名,* 号表示所有的类。
5、 * (…):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。
public class MyTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = (UserService) context.getBean("userService");
userService.search();
}
}
本质:动态代理
自定义类来实现Aop【切面定义】
业务类不变
切入类:
public class DiyPointcut {
public void before(){
System.out.println("---------方法执行前---------");
}
public void after(){
System.out.println("---------方法执行后---------");
}
}
<bean id="diy" class="com.xyzhang.config.DiyPointcut"/>
<aop:config>
<aop:aspect ref="diy">
<aop:pointcut id="diyPonitcut" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>
<aop:before pointcut-ref="diyPonitcut" method="before"/>
<aop:after pointcut-ref="diyPonitcut" method="after"/>
aop:aspect>
aop:config>
public class MyTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = (UserService) context.getBean("userService");
userService.add();
}
}
使用注解实现
@Aspect
public class AnnotationPointcut {
@Before("execution(* com.kuang.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("---------方法执行前---------");
}
@After("execution(* com.kuang.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("---------方法执行后---------");
}
@Around("execution(* com.kuang.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
System.out.println("签名:"+jp.getSignature());
//执行目标方法proceed
Object proceed = jp.proceed();
System.out.println("环绕后");
System.out.println(proceed);
}
}
<bean id="annotationPointcut" class="com.xyzhang.config.AnnotationPointcut"/>
<aop:aspectj-autoproxy/>
mybatis-spring
核心要点:Spring管理对象
四个属性(ACID原则):
配置声明式事务
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
bean>
结合AOP实现事务的织入
<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>
spring事务传播特性:事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。默认为REQUIRED
配置AOP
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.xyzhang.dao.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
aop:config>
测试
@Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserMapper mapper = (UserMapper) context.getBean("userDao");
List<User> user = mapper.selectUser();
System.out.println(user);
}