ssm

Spring的常用注解

传统的Spring做法是使用.xml文件来对bean进行注入或者是配置aop、事务,这么做有两个缺点:

1、如果所有的内容都配置在.xml文件中,那么.xml文件将会十分庞大;如果按需求分开.xml文件,那么.xml文件又会非常多。总之这将导致配置文件的可读性与可维护性变得很低。

2、在开发中在.java文件和.xml文件之间不断切换,是一件麻烦的事,同时这种思维上的不连贯也会降低开发的效率。

为了解决这两个问题,Spring引入了注解,通过"@XXX"的方式,让注解与Java Bean紧密结合,既大大减少了配置文件的体积,又增加了Java Bean的可读性与内聚性。

Spring的一个核心功能是IOC,就是将Bean初始化加载到容器中,Bean是如何加载到容器的,可以使用Spring注解方式或者Spring XML配置方式。

注解本身是没有功能的,和xml一样,注解和xml都是一种元数据,元数据即解释数据的数据,也就是所谓的配置。

1、声明bean的注解

@Component:泛指各种组件

@Controller、@Service、@Repository都可以称为@Component

@Controller:控制层

@Service:业务层

@Repository:数据访问层

2、注入bean的注解

@Autowired:由Spring提供

@Inject:由JSR-330提供

@Resource:由JSR-250提供

3、Java配置类相关注解

@Configuration:声明当前类为配置类

@Bean:注解在方法上,声明当前方法的返回值为一个bean,替代xml中的方式

@ComponentScan:用于对Component进行扫描

4、切面(AOP)相关注解

Spring支持AspectJ的注解式切面编程

@Aspect:声明一个切面

@After:在方法执行之后执行(方法上)

@Before:在方法执行之前执行(方法上)

@Around:在方法执行之前与之后执行(方法上)

@PointCut:声明切点

@EnableAspectJAutoProxy:开启Spring对AspectJ代理的支持

5、@Bean的属性支持

@Scope设置类型包括:设置Spring容器如何新建Bean实例

Singleton:单例,一个Spring容器中只有一个bean实例,默认模式

Protetype:每次调用新建一个bean

Request:web项目中,给每个http request新建一个bean

Session:web项目中,给每个http session新建一个bean

GlobalSession:给每一个 global http session新建一个Bean实例

6、@Value注解

注入普通字符、注入操作系统属性、注入表达式结果、注入其它bean属性、注入文件资源、注入网站资源、注入配置文件

7、环境切换

@Profile:指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件。

@Conditional:通过实现Condition接口,并重写matches方法,从而决定该bean是否被实例化。

*8、异步相关

@EnableAsync:配置类中通过此注解开启对异步任务的支持

@Async:在实际执行的bean方法使用该注解来声明其是一个异步任务(方法上或类上所有的方法都将异步,需要@EnableAsync开启异步任务)

9、定时任务相关

@EnableScheduling:在配置类上使用,开启计划任务的支持

@Scheduled:来申明这是一个任务,包括cron,fixDelay,fixRate等类型(方法上,需先开启计划任务的支持)

10、Enable注解说明

@EnableAspectAutoProxy:开启对AspectJ自动代理的支持

@EnableAsync:开启异步方法的支持

@EnableScheduling:开启计划任务的支持

@EnableWebMvc:开启web MVC的配置支持

@EnableConfigurationProperties:开启对@ConfigurationProperties注解配置Bean的支持

@EnableJpaRepositories:开启对SpringData JPA Repository的支持

@EnableTransactionManagement:开启注解式事务的支持

@EnableCaching:开启注解式的缓存支持

11、测试相关注解

@RunWith:运行器,Spring中通常用于对JUnit的支持

@ContextConfiguration

常用注解

二、SpringMVC常用注解

1、@EnableWebMvc:在配置类中开启Web MVC的配置支持

2、@Controller

3、@RequestMapping:用于映射web请求,包括访问路径和参数

4、@ResponseBody:支持将返回值放到response内,而不是一个页面,通常用户返回json数据

5、@RequestBody:允许request的参数在request体中,而不是在直接连接的地址后面

6、@PathVariable:用于接收路径参数,比如@RequestMapping(“/hello/{name}”)声明的路径,将注解放在参数前,即可获取该值,通常作为Restful的接口实现方法。

7、@RestController:该注解为一个组合注解,相当于@Controller和@ResponseBody的组合,注解在类上,意味着,该Controller的所有方法都默认加上了@ResponseBody。

8、@ControllerAdvice:全局异常处理、全局数据绑定、全局数据预处理

9、@ExceptionHandler:用于全局处理控制器里的异常

10、@InitBinder:用来设置WebDataBinder,WebDataBinder用来自动绑定前台请求参数到Model中

Spring 的 IOC 和 AOP 是什么,有哪些优点?

Spring 的 IOC 和 AOP 是什么,有哪些优点?

Tomcat 是 Servlet 容器,只负责管理 Servlet。我们平常使用的组件则需要另一种容器来管理,这种容器我们称之为 IoC 容器。

Spring 支持的控制反转(Inversion of Control,缩写为IoC)和面向切面编程(Aspect-oriented programming,缩写为AOP

控制反转和依赖注入

控制反转是指对象的创建和配置的控制权从调用方转移给容器。好比在家自己做菜,菜的味道全部由自己控制;去餐馆吃饭,菜的味道则是交由餐馆控制。IoC 容器就担任了餐馆的角色。

有了 IoC 容器,我们可以将对象交由容器管理,交由容器管理后的对象称之为 Bean。调用方不再负责组件的创建,要使用组件时直接获取 Bean 即可:

@Component
public class UserServiceImpl implements UserService{
    @Autowired // 获取 Bean
    private UserDao userDao;
}

调用方只需按照约定声明依赖项,所需要的 Bean 就自动配置完毕了,就好像在调用方外部注入了一个依赖项给其使用,所以这种方式称之为 依赖注入(Dependency Injection,缩写为 DI)。控制反转和依赖注入是一体两面,都是同一种开发模式的表现形式

对象交由容器管理后,默认是单例的,这就解决了资源浪费问题。

若要更换实现类,只需更改 Bean 的声明配置,即可达到无感知更换:

public class UserServiceImpl implements UserService{
    ...
}
 
// 将该实现类声明为 Bean
@Component
public class OtherUserServiceImpl implements UserService{
    ...
}

面向对象的局限性

面向对象编程(Object-oriented programming,缩写:OOP)的三大特性:封装、继承、多态,我们早已用得炉火纯青。OOP 的好处已无需赘言,相信大家都有体会。这里咱们来看一下 OOP 的局限性。

当有重复代码出现时,可以就将其封装出来然后复用。我们通过分层、分包、分类来规划不同的逻辑和职责,就像之前讲解的三层架构。但这里的复用的都是核心业务逻辑,并不能复用一些辅助逻辑,比如:日志记录、性能统计、安全校验、事务管理,等等。这些边缘逻辑往往贯穿你整个核心业务,传统 OOP 很难将其封装:

public class UserServiceImpl implements UserService {
    @Override
    public void doService() {
        System.out.println("---安全校验---");
        System.out.println("---性能统计 Start---");
        System.out.println("---日志打印 Start---");
        System.out.println("---事务管理 Start---");
 
        System.out.println("业务逻辑");
 
        System.out.println("---事务管理 End---");
        System.out.println("---日志打印 End---");
        System.out.println("---性能统计 End---");
    }
}

OOP 是至上而下的编程方式,犹如一个树状图,A调用B、B调用C,或者A继承B、B继承C。这种方式对于业务逻辑来说是合适的,通过调用或继承以复用。而辅助逻辑就像一把闸刀横向贯穿所有方法,如图2-4所示:

这一条条横线仿佛切开了 OOP 的树状结构,犹如一个大蛋糕被切开多层,每一层都会执行相同的辅助逻辑,所以大家将这些辅助逻辑称为层面或者切面。

img

代理模式用来增加或增强原有功能再适合不过了,但切面逻辑的难点不是不修改原有业务,而是对所有业务生效。对一个业务类增强就得新建一个代理类,对所有业务增强,每个类都要新建代理类,这无疑是一场灾难。而且这里只是演示了一个日志打印的切面逻辑,如果我再加一个性能统计切面,就得新建一个切面代理类来代理日志打印的代理类,一旦切面多起来这个代理类嵌套就会非常深。

面向切面编程

AOP 不是 OOP 的对立面,它是对 OOP 的一种补充。OOP 是纵向的,AOP 是横向的,两者相结合方能构建出良好的程序结构。AOP 技术,让我们能够不修改原有代码,便能让切面逻辑在所有业务逻辑中生效

@Aspect // 声明一个切面
@Component
public class MyAspect {
    // 原业务方法执行前
    @Before("execution(public void com.rudecrab.test.service.*.doService())")
    public void methodBefore() {
        System.out.println("===AspectJ 方法执行前===");
    }
 
    // 原业务方法执行后
    @AfterReturning("execution(* com.rudecrab.test.service..doService(..))")
    public void methodAddAfterReturning() {
        System.out.println("===AspectJ 方法执行后===");
    }
}

IoC 解决了以下问题:

  • 创建了许多重复对象,造成大量资源浪费;
  • 更换实现类需要改动多个地方;
  • 创建和配置组件工作繁杂,给组件调用方带来极大不便。

AOP 解决了以下问题:

  • 切面逻辑编写繁琐,有多少个业务方法就需要编写多少次。

Spring 框架用到了哪些设计模式?

Spring 框架用到了哪些设计模式?

简单工厂由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类。

Spring中的BeanFactory就是简单工厂模式的体现,根据传入一个唯一的标识来获得Bean对象,但是否是在传入参数后创建还是传入参数前创建这个要根据具体情况来定。

工厂方法:

实现了FactoryBean接口的bean是一类叫做factory的bean。其特点是,spring会在使用getBean()调
用获得该bean时,会自动调用该bean的getObject()方法,所以返回的不是factory这个bean,而是这个
bean.getOjbect()方法的返回值。

单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点

spring对单例的实现: spring中的单例模式完成了后半句话,即提供了全局的访问点BeanFactory。但没有从构造器级别去控制单例,这是因为spring管理的是任意的java对象。

适配器模式

Spring定义了一个适配接口,使得每一种Controller有一种对应的适配器实现类,让适配器代替
controller执行相应的方法。这样在扩展Controller时,只需要增加一个适配器类就完成了SpringMVC 的扩展了。

装饰器模式:动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类
更为灵活

Spring中用到的包装器模式在类名上有两种表现:一种是类名中含有Wrapper,另一种是类名中含有 Decorator。

动态代理:

切面在应用运行的时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象创建动态的创建一个代理对象。SpringAOP就是以这种方式织入切面的。
织入:把切面应用到目标对象并创建新的代理对象的过程。

观察者模式

spring的事件驱动模型使用的是 观察者模式 ,Spring中Observer模式常用的地方是listener的实现。

策略模式:

Spring框架的资源访问Resource接口。该接口提供了更强的资源访问能力,Spring 框架本身大量使用了 Resource接口来访问底层资源。

介绍 Spring Bean 的生命周期

介绍 Spring Bean 的生命周期

一、简介
Spring Bean 的生命周期在整个 Spring 中占有很重要的位置,从BeanFactory或ApplicationContext取得的实例为Singleton,也就是预设为每一个Bean的别名只能维持一个实例,而不是每次都产生一个新的对象使用Singleton模式产生单一实例,在spring中,singleton属性默认是true,只有设定为false,则每次指定别名取得的Bean时都会产生一个新的实例,Spring只帮我们管理单例模式Bean的完整生命周期,对于prototype的bean,Spring在创建好交给使用者之后则不会再管理后续的生命周期。

二、图例
生命周期图如下:img

也可以概括为:

img

三、图例说明
1、实例化一个Bean
2、按照Spring上下文对实例化的Bean进行配置,也就是IOC注入
3、如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String)方法,传递的参数就是Spring配置文件中Bean的id值img4、如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(BeanFactory),传递的是Spring工厂自身

img

5、如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文

img

6、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法,
BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在Bean初始化结束时调用那个的方法,也可以被应用于内存或缓存技术;

img

7、如果Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法。
8、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法
9、当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用那个其实现的destroy()方法;

img

10、最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。

MyBatis 如何实现延迟加载?

MyBatis 如何实现延迟加载?

一、延迟加载

1、在mybatis.xml配置文件中,开启延迟加载

<settings>
  <!--开启延迟加载-->
  <setting name="lazyLoadingEnabled" value="true"></setting>
  <setting name="aggressiveLazyLoading" value="false"></setting>
  <!--延迟加载触发方法,equals、hashCode、toString都会触发加载-->
  <setting name="lazyLoadTriggerMethods" value="hashCode"></setting>
  <!--数据库下划线(_)命名转驼峰命名-->
  <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

2、配置mapper文件

1、一对一

* 一方

  <resultMap id="studentGradeById" type="Student">
    <id column="id" property="id"></id>
    <result column="name" property="name"></result>
    <result column="age" property="age"></result>
    <result column="sex" property="sex"></result>          <!--关闭延迟加载会做两次查询-->
    <association column="grade_id" property="grade" javaType="Grade"
           select="com.wuxi.daos.GradeMapper.selectById"></association>
  </resultMap>
  <select id="selectStudentGradeById" resultMap="studentGradeById">
    select * from student where id = #{id}
  </select>

* 另一方

  <select id="selectById" resultType="Grade">
    select * from grade where id = #{id}
  </select>

* 测试

Student student = smapper.selectStudentGradeById(4);
System.out.println(student);
// student.hashCode();
System.out.println(student.getGrade());

2、一对多

* 一方

  <resultMap type="Grade" id="gradeStudents">
    <id column="id" property="id"></id>
    <result column="name" property="name"></result>          <!--关闭延迟加载会做两次查询-->
    <collection property="students" ofType="Student" column="id"
          select="com.wuxi.daos.StudentMapper.selectStudentsByGrade"></collection>
  </resultMap>
  <select id="selectById" resultMap="gradeStudents">
    select * from grade where id = #{id}
  </select>

* 多方

<select id="selectStudentsByGrade" resultType="Student">
  select * from student where grade_id=#{grade_id}
</select>

* 测试

Grade grade = gmapper.selectById(1);
System.out.println(grade);
// student.hashCode();
System.out.println(grade.getStudents());

介绍 MyBatis 的多级缓存机制

介绍 MyBatis 的多级缓存机制

二、缓存

1、一级缓存

1、概念

一级缓存是SqlSession范围的缓存,当调用SqlSession的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。

2、测试

// Student student1 = smapper.selectStudentGradeById(1);
// Student student2 = smapper.selectStudentGradeById(1);
// System.out.println(student1 == student2); // true
// ********************************
Student student1 = smapper.selectStudentGradeById(1);
Student student = new Student();
student.setName("杜兰特");
student.setAge(28);
student.setSex(1);
smapper.insertStudent(student);
Student student2 = smapper.selectStudentGradeById(1);
System.out.println(student1 == student2); // false

2、二级缓存

1、开启二级缓存

1、对象需要实现Serializable接口

2、在mybatis.xml配置文件中,开启二级缓存

3、配置mapper文件

2、测试

SqlSession sqlSession1 = sqlSessionFactory.openSession();
StudentMapper mapper1 = sqlSession1.getMapper(StudentMapper.class);
Student student1 = mapper1.selectStudentGradeById(1);
sqlSession1.close();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
StudentMapper mapper2 = sqlSession2.getMapper(StudentMapper.class);
Student student2 = mapper2.selectStudentGradeById(1);
sqlSession2.close();
// 只查询了一次数据库。二级缓存存储的是数据,并不是对象
System.out.println(student1 == student2); // false

posted on 2022-06-26 14:01  Steam残酷  阅读(31)  评论(0)    收藏  举报