在一些普通的工具类或者POJO类需要注入service或者repository等spring管理的资源进行一定的逻辑处理,普通类不受spring管理,所以利用@Autowired实现不了。本文介绍几种方式解决此问题。
本文利用老师与学生的关系作为例子讲解,一个老师可以管理多个学生,在查询老师列表时也将他名下的学生查询出来,Teacher表与Student不进行关联表处理,那么就需要在Teacher的Entity里注入StudentService进行查询。
Student
uuid | deleted | status | teacher
------+---------+----------+---------
s1 | | active | t1
s2 | deleted | inactive | t1
(2 rows)
Teacher
uuid
------
t1
t2
(2 rows)
Teacher
不同的实现方法有不同的注入,具体的参考下文
Student
@Data
@Entity
@Table(name = "student")
@EqualsAndHashCode(of = {"id"})
public class Student {
@Column(name = "uuid")
@Id
private String id;
@Column(name = "deleted")
private String deleted;
@Column(name = "status")
private String status;
@Column(name = "teacher")
private String teacher;
}
StudentReposity
@Repository
public interface StudentReposity extends CrudRepository<Student, String>{
@Query("select s from Student s where s.teacher=:teacherId")
public Set<Student> findStudentsByTeacher(@Param("teacherId") String teacherId);
}
TeacherRepository
@Repository
public interface TeacherRepository extends CrudRepository<Teacher, String>{
@Query("select t from Teacher t")
public Set<Teacher> findTeachers();
}
StudentService
@Service
public class StudentService {
@Autowired
StudentReposity studentReposity;
public Set<Student> findStudentsByTeacher(String teacherId){
return studentReposity.findStudentsByTeacher(teacherId);
}
}
TeacherService
@Service
public class TeacherService {
@Autowired
TeacherRepository teacherRepository;
public Set<Teacher> findTeachers(){
return teacherRepository.findTeachers();
}
}
@RestController
public class TeacherController {
@Autowired
TeacherService teacherService;
@GetMapping("/teachers")
public Set<Teacher> findTeachers(){
return teacherService.findTeachers();
}
}
这种方法比较简单,但是如果你有多个Entity需要注入多个service的时候就比较麻烦一点,另外sonarqube代码检测也会有警告。
Entity
@Data
@Entity
@Table(name = "teacher")
@EqualsAndHashCode(of = {"id"})
public class Teacher {
@Column(name = "uuid")
@Id
private String id;
@Transient
private Set<Student> students;
public Set<Student> getStudents(){
return SpringBeanUtil.staticStudentService.findStudentsByTeacher(getId());
}
}
利用@PostConstruct
@Component
public class SpringBeanUtil {
@Autowired
StudentService studentService;
public static StudentService staticStudentService;
@PostConstruct
public void init() {
staticStudentService = studentService;
}
}
Entity
@Data
@Entity
@Table(name = "teacher")
@EqualsAndHashCode(of = {"id"})
public class Teacher {
@Column(name = "uuid")
@Id
private String id;
@Transient
private Set<Student> students;
public Set<Student> getStudents(){
return EnumBeanUtil.BEAN.getBean(StudentService.class).findStudentsByTeacher(getId());
}
}
定义获取ApplicatinContext的Enum
public enum EnumBeanUtil {
BEAN;
private ApplicationContext applicationContext;
public ApplicationContext getApplicationContext() {
return applicationContext;
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
}
设置ENUM的applicationContext变量
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
EnumBeanUtil.BEAN.setApplicationContext(SpringApplication.run(Application.class, args));
}
}
启动测试,可以得到studnets。
Entity
@Data
@Entity
@Table(name = "teacher")
@EqualsAndHashCode(of = {"id"})
public class Teacher {
@Column(name = "uuid")
@Id
private String id;
@Transient
private Set<Student> students;
public Set<Student> getStudents(){
StudentService studentService = BeanUtil.getBean(StudentService.class);
return studentService.findStudentsByTeacher(getId());
}
}
1、实现ApplicationContextAware接口
@Component
public class BeanUtil implements ApplicationContextAware{
//@note
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
BeanUtil.context = applicationContext;
}
public static <T> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
}
@note: 如果你的代码有sonarqube检查的话会有critical的警告,所以如果你有严格的代码规范的话这种方法不推荐。
测试
发起/teachers请求可得到结果:
[
{
"id": "t1",
"students": [
{
"id": "s1",
"deleted": null,
"status": "active",
"teacher": "t1"
},
{
"id": "s2",
"deleted": "deleted",
"status": "inactive",
"teacher": "t1"
}
]
},
{
"id": "t2",
"students": []
}
]
可以看到students查出来,证明StudnetService确实被注入进去了。
Entity
@Data
@Entity
@Table(name = "teacher")
@EqualsAndHashCode(of = {"id"})
public class Teacher {
@Column(name = "uuid")
@Id
private String id;
@Transient
private Set<Student> students;
@Transient
@JsonIgnore
private StudentService studentService;
public Set<Student> getStudents(){
return studentService.findStudentsByTeacher(getId());
}
}
1、创建自己的Interceptor并扩展EmptyInterceptor
public class StudentInterceptor extends EmptyInterceptor{
/**
*
*/
private static final long serialVersionUID = -4771865788342461458L;
@Autowired
@Lazy //@note1
StudentService studentService;
//@note2
@Override
public boolean onLoad(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
//@note3
if(entity instanceof Teacher) {
((Teacher) entity).setStudentService(studentService);
return true;
}
return false;
}
}
@note1: 懒加载防止启动的时候出现bean加载死循环
@note2:重写onLoad方法,在对象加载的时候就注入service。
@note3: 对象是Teacher的注入studentService。如果对象多可以在这里添加自己的需求。
2、实例化自定义的Interceptor
如果是注解方式配置的话:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "entityManagerFactory"
, basePackages = { "***.your.repository" })
public class PersistenceConfiguration {
@Primary
@Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder,
@Qualifier("dataSource") DataSource dataSource) {
//@note
Map<String, Object> jpaProperties = new HashMap<>();
jpaProperties.put("hibernate.ejb.interceptor", studentInterceptor());
return builder.properties(jpaProperties)
//your other configuration
.build();
}
@Bean
public StudentInterceptor studentInterceptor() {
return new StudentInterceptor();
}
//other configuration
}
如果你的Spring boot版本号小于2,可以通过extends HibernateJpaAutoConfiguration 来实现,具体做法参考link.其他做法也可以参考该文章,但是我本人测都实现不了。
测试
发起/teachers请求可得到结果:
[
{
"id": "t1",
"students": [
{
"id": "s1",
"deleted": null,
"status": "active",
"teacher": "t1"
},
{
"id": "s2",
"deleted": "deleted",
"status": "inactive",
"teacher": "t1"
}
]
},
{
"id": "t2",
"students": []
}
]
可以看到students查出来,证明StudnetService确实被注入进去了。
Entity
声明注解@Configurable、@Aspect。
@Data
@Entity
@Table(name = "teacher")
@EqualsAndHashCode(of = {"id"})
@Configurable
@Aspect
public class Teacher {
@Column(name = "uuid")
@Id
private String id;
@Transient
private Set<Student> students;
@Transient
@JsonIgnore
@Autowired
private StudentService studentService;
public Set<Student> getStudents(){
return studentService.findStudentsByTeacher(getId());
}
}
1、告诉spring激活aop injection
@SpringBootApplication
@EnableSpringConfigured //@note。通知spring
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
2、使用AspectJ “Java Agent”
如果你是使用Eclipse,可以在Debug Configurations->Arguments->VM arguments添加==-javaagent:\your\path\to\aspectjweaver.jar==。需要下载aspectjweaver依赖。
如果你是直接打好包,则可执行java -jar your.jar -javaagent:\your\path\to\aspectjweaver.jar。
如果你是xml配置的话可以参考这篇文章.
要实现AOP可能还需要导入一些依赖:
org.springframework.boot
spring-boot-starter-aop
2.2.6.RELEASE
如果你还有其他方法,欢迎讨论*-*
https://blog.csdn.net/qq_39056805/article/details/80285915
https://blog.csdn.net/Mrs_chens/article/details/93741730
https://stackoverflow.com/questions/9666922/enable-spring-aop-or-aspectj
https://dzone.com/articles/inject-spring-beans-aspectj
https://stackoverflow.com/questions/27744287/what-is-the-spring-java-config-equivalent-of-contextspring-configured
https://stackoverflow.com/questions/10008714/requested-bean-is-currently-in-creation-is-there-an-unresolvable-circular-refer
https://stackoverflow.com/questions/25283767/how-to-use-spring-managed-hibernate-interceptors-in-spring-boot/29919009
http://jblewitt.com/blog/?p=129