搭建Spring Boot2.X集成Hibernate5项目,并集成传统SSH老项目的安全认证组件,以Spring Boot方式开发项目并集成到老系统
场景
由于老项目(SSH架构)需要新添加功能模块,习惯了Spring Boot快速开发,突然使用XML那一套东西,不是不可以,但是不爽,故想使用Spring Boot方式开发新项目集成到老系统。
同时,记录、分享在搭建、集成方面遇到的问题。
可行性分析
1.能否集成?
传统项目(SSH架构)那一套就是XML的配置,Spring Boot则是将XML简化,也无非类似,且新Spring Boot项目核心是提供接口被调用。故只要能保证开发的Spring Boot项目能成功集成到SSH老系统即可。
2.安全如何认证?
老项目存在权限认证组件,新开发的Spring Boot项目肯定得做集成,由老系统的认证组件进行安全认证。老系统的安全认证组件是非SpringBoot开发,但是以Jar的形式提供使用,最大问题在于相关配置文件中的对象的初始化以及兼容性问题需要考虑。
3.如何进行 ?
由于老系统使用Hibernate,所以首先就需要搭建Spring Boot集成Hibernate项目。
Hibernate没有Spring Boot的相关启动器,做集成稍显麻烦,但是万变不离其中,毕竟Spring Boot都是从xml转变过来的。
其次在搭建好的环境基础上引入老系统的安全认证组件进行测试。
搭建Spring Boot集成Hibernate5项目
添加依赖
JPA的默认实现是Hibernate,直接引入spring-boot-starter-data-jpa
启动器即可。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
Entity
创建一个User对象,与数据库数据表映射。
@Entity
@Data
@Table(name="user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private Integer age;
}
Dao
注意:低版本的Hibernate(应该是5以下)使用sessionFactory
对象获取会话对象,高版本使用的是EntityManagerFactory
对象。由于老项目肯定使用sessionFactory
对象,所以集成测试也使用sessionFactory
对象,此时sessionFactory是有警告。
public interface IUserDao {
/**
* @description: 保存用户
**/
void saveUser(User user);
User getUser(Integer id);
}
@Repository
public class UserDaoImpl implements IUserDao {
@Autowired
private SessionFactory sessionFactory;
Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
@Override
public void saveUser(User user) {
getCurrentSession().save(user);
}
@Override
public User getUser(Integer id) {
return getCurrentSession().get(User.class, id);
}
}
高版本中这样获取会话对象:
@Autowired
private EntityManagerFactory entityManagerFactory;
Session getCurrentSession() {
return entityManagerFactory.unwrap(SessionFactory.class).getCurrentSession();
}
Service
public interface UserService {
void mySave(User user);
User getUser(Integer id);
}
@Transactional(rollbackFor = Exception.class)
@Service
public class UserServiceImpl implements UserService {
@Autowired
private IUserDao userDao;
@Override
public void mySave(User user) {
userDao.saveUser(user);
}
@Override
public User getUser(Integer id) {
return userDao.getUser(id);
}
}
Controller
@RestController
@RequestMapping("/test")
public class Controller {
@Autowired
private UserService userService;
/**
* 添加
*/
@RequestMapping("/insert")
public String insert() {
User user = new User();
user.setName("小白");
user.setAge(22);
userService.mySave(user);
return "success";
}
/**
* 查询
*/
@RequestMapping("/get")
public User getUser(Integer id) {
return userService.getUser(id);
}
}
配置application.properties
server.port=8888
#----------------------数据库配置------------------------------
spring.datasource.url=jdbc:mysql://localhost:3306/demo?characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456
启动类
排除HibernateJpa相关的自动配置,进行手动配置,故需排除HibernateJpaAutoConfiguration
自动配置类,否则报错如下:
org.springframework.orm.jpa.EntityManagerHolder cannot be cast to org.springframework.orm.hibernate5.SessionHolder
@SpringBootApplication(exclude = {HibernateJpaAutoConfiguration.class})
public class DemoApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
配置Hibernate
手动配置配置会话工厂与配置事务管理器,具体代码参考如下:
@Configuration
public class HibernateConfig {
@Autowired
private DataSource dataSource;
/**
* 配置会话工厂
*/
@Bean(name = "sessionFactory")
// @Bean(name="entityManagerFactory")
public SessionFactory sessionFactoryBean() throws IOException {
LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
// 设置数据源
sessionFactoryBean.setDataSource(dataSource);
// entity包路径
sessionFactoryBean.setPackagesToScan("cn.ybzy.demo.entity");
// 配置hibernate属性
Properties properties = new Properties();
// sql方言
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
// 自动创建|更新|验证数据库表结构
properties.setProperty("hibernate.hbm2ddl.auto", "update");
// 输出sql到控制台
properties.setProperty("hibernate.show_sql", "true");
// 打印漂亮的sql
properties.setProperty("hibernate.format_sql", "true");
properties.setProperty("hibernate.current_session_context_class", "org.springframework.orm.hibernate5.SpringSessionContext");
sessionFactoryBean.setHibernateProperties(properties);
sessionFactoryBean.afterPropertiesSet();
SessionFactory sessionFactory = sessionFactoryBean.getObject();
return sessionFactory;
}
/**
* 配置事务管理器
*/
@Bean(name = "transactionManager")
public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
HibernateTransactionManager transactionManager = new HibernateTransactionManager();
// 设置 sessionFactory
transactionManager.setSessionFactory(sessionFactory);
return transactionManager;
}
/**
* 创建拦截器,配置事务拦截方式
* 指定事务管理器和设置事务属性
*
* @return
*/
// @Bean
// public TransactionInterceptor transactionInterceptors(HibernateTransactionManager transactionManager) {
// TransactionInterceptor transInterceptor = new TransactionInterceptor();
// // 设置事务管理器
// transInterceptor.setTransactionManager(transactionManager);
// // 设置方法事务属性
// Properties props = new Properties();
// props.setProperty("save*", "PROPAGATION_REQUIRED");
// props.setProperty("update*", "PROPAGATION_REQUIRED");
// props.setProperty("delete*", "PROPAGATION_REQUIRED");
// props.setProperty("find*", "PROPAGATION_REQUIRED,readOnly");
// props.setProperty("get*", "PROPAGATION_REQUIRED,readOnly");
// transInterceptor.setTransactionAttributes(props);
// return transInterceptor;
// }
//
// /**
// * AOP拦截配置
// * 创建一个自动代理Bean的对象
// */
// @Bean
// public BeanNameAutoProxyCreator beanNameAutoProxyCreator() {
// BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
// // 设置应该自动被代理包装的 bean 的名称
// beanNameAutoProxyCreator.setBeanNames("*ServiceImpl");
// // 设置常用拦截器
// beanNameAutoProxyCreator.setInterceptorNames("transactionInterceptor");
// return beanNameAutoProxyCreator;
// }
}
进行测试
1.执行新增测试
2.执行查询测试
3.执行新增异常事务回滚测试
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Mon Aug 15 14:44:05 CST 2022
There was an unexpected error (type=Internal Server Error, status=500).
/ by zero
java.lang.ArithmeticException: / by zero
at cn.ybzy.demo.service.impl.MyUserServiceImpl.saveUser(MyUserServiceImpl.java:25)
at cn.ybzy.demo.service.impl.MyUserServiceImpl$$FastClassBySpringCGLIB$$f77af9f1.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:771)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:367)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691)
at cn.ybzy.demo.service.impl.MyUserServiceImpl$$EnhancerBySpringCGLIB$$a2877662.saveUser(<generated>)
当上述测试验证操作完成后进行安全认证组件集成。
集成安全认证组件
集成分析
由于添加的安全认证组件涉及到了2个xml配置文件
spring-shiro.xml
spring-datasource-jedis.xml
xml文件里面创建的Bean需要通过Spring Boot的方式添加到容器中
解决方案:
1.使用@ImportResource注解将原生配置文件引入,进行自动配置其中定义的Bean
2.参考xml中的配置,将xml配置转换成类配置
开始集成
这里使用第一种方案。由于引入组件中定义了一些对象,如service类 Dao类,这些对象需要加入到Spring容器中,所以使用@ComponentScan(basePackages = {"com.XX"})
方式进行扫描
@ImportResource(locations = {"classpath:spring-shiro.xml","classpath:spring-datasource-jedis.xml"})
@ComponentScan(basePackages = {"com.XXX"})
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
启动报错
Description:
Field sessionFactory in cn.ybzy.base.dao.impl.UserDaoImpl required a bean of type 'org.hibernate.SessionFactory' that could not be found.
The injection point has the following annotations:
- @org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'org.hibernate.SessionFactory' in your configuration.
原因如下:
进行Spring扫描时,发现老项目Dao层中的需要SessionFactory对象,毕竟需要该对象操作数据库,但是此刻配置HibernateConfig对象未生效,故找不到
解决方案:将@ComponentScan(basePackages = {"com.XXX"})
从启动类移动到HibernateConfig
类
@Configuration
@ComponentScan(basePackages = {"com.XXX"})
public class HibernateConfig {
}
再次出现异常:
Caused by: org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'userDaoImpl' for bean class [cn.ybzy.base.dao.impl.UserDaoImpl] conflicts with existing, non-compatible bean definition of same name and class [cn.ybzy.demo.repository.impl.UserDaoImpl]
原因:
认证组件中定义有UserDaoImpl类与新项目中定义的测试UserDaoImpl类冲突,故将Spring Boot项目中的相关的测试对象,如UserDaoImpl改个名称
public interface IMyUserDao {
/**
* @description: 保存用户
**/
void saveUser(User user);
}
@Repository
public class MyUserDaoImpl implements IMyUserDao {
@Autowired
private SessionFactory sessionFactory;
Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
@Override
public void saveUser(User user) {
getCurrentSession().save(user);
}
}
启动成功
2022-08-05 14:49:52.611 INFO 3048 --- [ restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8888 (http) with context path ''
2022-08-05 14:49:52.613 INFO 3048 --- [ restartedMain] o.s.s.quartz.SchedulerFactoryBean : Starting Quartz Scheduler now
2022-08-05 14:49:52.613 INFO 3048 --- [ restartedMain] org.quartz.core.QuartzScheduler : Scheduler quartzScheduler_$_NON_CLUSTERED started.
2022-08-05 14:49:52.626 INFO 3048 --- [ restartedMain] s.a.ScheduledAnnotationBeanPostProcessor : No TaskScheduler/ScheduledExecutorService bean found for scheduled processing
2022-08-05 14:49:52.635 INFO 3048 --- [ restartedMain] cn.ybzy.demo.DemoApplication : Started DemoApplication in 7.301 seconds (JVM running for 8.798)
测试验证
接着测试权限认证是否正常。由于认证组件使用Shiro,故此处Shiro报错,集成认证组件出现异常
org.apache.shiro.UnavailableSecurityManagerException: No SecurityManager accessible to the calling code, either bound to the org.apache.shiro.util.ThreadContext or as a vm static singleton. This is an invalid application configuration.
经过一番折腾,在配置delegatingFilterProxy对象即可解决
@ImportResource(locations = {"classpath:spring-shiro.xml","classpath:spring-datasource-jedis.xml"})
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean
public ShiroFilterFactoryBean delegatingFilterProxy(@Qualifier("shiroFilter") ShiroFilterFactoryBean shiroFilterFactoryBean) {
return shiroFilterFactoryBean;
}
}
启动项目,执行测试,成功拦截
访问登录接口,得到安全组件设置的cookie值:
set-cookie SSO-SESSIONID=eb22cc107b094fb2ae5884a8df7923f4; Path=/; HttpOnly; SameSite=lax
将cookie值添加到集成项目对应的浏览器中
执行新增操作
执行查询操作
执行事务操作
@Override
public void mySave(User user) {
userDao.saveUser(user);
int a=1/0;
}
程序异常,同时数据库事务生效。
使用JPA功能
Dao
创建相关接口继承CrudRepository
对象
@Repository
public interface IUserRepository extends CrudRepository<User, Integer> {}
Service
@Transactional(rollbackFor = Exception.class)
@Service
public class UserServiceImpl implements UserService {
@Autowired
private IUserDao userDao;
@Autowired
private IUserRepository userRepository;
@Override
public void saveUser(User user) {
// userDao.saveUser(user);
userRepository.save(user);
int a = 1 / 0;
}
@Override
public User getUser(Integer id) {
// return userDao.getUser(id);
Optional<User> optional = userRepository.findById(id);
return optional.get();
}
}
配置application.properties
server.port=8888
#----------------------数据库配置------------------------------
spring.datasource.url=jdbc:mysql://localhost:3306/demo?characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456
#----------------------JPA配置------------------------------
# 数据库类型
spring.jpa.database=MySQL
# 打印sql语句
spring.jpa.show-sql=true
# 生成SQL表
spring.jpa.generate-ddl=true
# 自动修改表结构
spring.jpa.hibernate.ddl-auto=update
启动项目
启动报错:
Description:
Field userRepository in cn.ybzy.demo.service.impl.UserServiceImpl required a bean named 'entityManagerFactory' that could not be found.
The injection point has the following annotations:
- @org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean named 'entityManagerFactory' in your configuration.
默认情况下,JPA通过名称entityManagerFactory
搜索sessionFactory
手动设置:方法上添加 @Bean(name="entityManagerFactory")
自动设置:方法名字一定要是entityManagerFactory
Hibernate配置
/**
* 配置会话工厂
*/
// @Bean(name = "sessionFactory")
@Bean(name="entityManagerFactory")
public SessionFactory sessionFactoryBean() throws IOException {
LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
// 设置数据源
sessionFactoryBean.setDataSource(dataSource);
// entity包路径
sessionFactoryBean.setPackagesToScan("cn.ybzy.demo.entity");
// 配置hibernate属性
Properties properties = new Properties();
// sql方言
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
// 自动创建|更新|验证数据库表结构
properties.setProperty("hibernate.hbm2ddl.auto", "update");
// 输出sql到控制台
properties.setProperty("hibernate.show_sql", "true");
// 打印漂亮的sql
properties.setProperty("hibernate.format_sql", "true");
properties.setProperty("hibernate.current_session_context_class", "org.springframework.orm.hibernate5.SpringSessionContext");
sessionFactoryBean.setHibernateProperties(properties);
sessionFactoryBean.afterPropertiesSet();
SessionFactory sessionFactory = sessionFactoryBean.getObject();
return sessionFactory;
}
获取会话
在JPA中通常使用EntityManagerFactory
对象获取会话。
@Autowired
private EntityManagerFactory entityManagerFactory;
Session getCurrentSession() {
return entityManagerFactory.unwrap(SessionFactory.class).getCurrentSession();
}
执行测试
最后进行测试,JPA功能正常。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/136877.html