编程知识 cdmana.com

Spring cloud eliminates circular dependency

Preface

Have you ever encountered project startup failure due to cyclic dependency in development ? Are there any difficulties in the process of troubleshooting circular dependencies ? How to avoid writing loop dependent code ?

I haven't written code for circular dependencies , As head of stability , I've checked many times .

Some simple logic code , Cyclic dependencies are easy to check . however , Our business is super complex , The vast majority of circular dependencies , I couldn't find out all day .

At first we came across a circular dependency dealing with a , As head of stability , What technology can do , It won't make people do it a second time , So , I wrote a circular dependency patrol code , Kill the cyclic dependency in the test environment .

 

The following is the scene and processing ideas , Not necessarily the best , Welcome to exchange .

background

SpringCloud When the service comes online BeanCurrentlyInCreationException abnormal ( There is no exception when the service is started locally , The test environment starts without exception , Sometimes it's abnormal when you go online ).

1, The local simulation is as follows :

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name  'studentA' : Bean with name  'studentA'  has been injected into other beans [studentC] in its raw version as part of a circular reference, but has eventually been wrapped.
This means that said other beans  do  not use the  final  version of the bean.
This is often the result of over-eager type matching - consider using  'getBeanNamesOfType'  with the  'allowEagerInit'  flag turned off,  for  example.
 

2, The production scenario is shown in the figure below :

 

Troubleshooting & simulation

  After production screening and local testing , The scenario in which this exception occurs is as follows :

1, If the class method has @Async annotation , The above exception may occur

2, If there is a circular dependency , And class methods have @Async annotation , It is inevitable that the above abnormalities will occur .

1, Scene demonstration :

  stay spring in , be based on field The circular dependency of attributes is OK :

 

Sample code :

@Service
@Slf4j
public class StudentA {

    @Autowired
    private StudentB studentB;
    public String test(){
        return studentB.test();
    }
    public String test1(){
        return "Studenta1";
    }
}

@Slf4j
@Service
public class StudentB {

    @Autowired
    private StudentC studentC;

    public String test() {
        return "Studentb";
    }

    public String test1() {
        return studentC.test1();
    }
}

@Slf4j
@Service
public class StudentC {

    @Autowired
    private StudentA studentA;

    public String test() {
        return studentA.test();
    }

    public String test1() {
        return "Studentc1";
    }
}

@Autowired
private StudentA studentA ;
@Test
public void testA(){
    String v= studentA.test();
    log.info("testa={}",v);
}

The above code output is normal

2, Exception demonstration

If our approach adds @Async annotation . Throw an exception :

@Service
@Slf4j
public class StudentA {

    @Autowired
    private StudentB studentB;
    @Async
    public String test(){
        return studentB.test();
    }
    @Async
    public String test1(){
        return "Studenta1";
    }
}

@Slf4j
@Service
public class StudentB {

    @Autowired
    private StudentC studentC;
    @Async
    public String test() {
        return "Studentb";
    }
    @Async
    public String test1() {
        return studentC.test1();
    }
}

@Slf4j
@Service
public class StudentC {

    @Autowired
    private StudentA studentA;
    @Async
    public String test() {
        return studentA.test();
    }
    @Async
    public String test1() {
        return "Studentc1";
    }
}

@Autowired
private StudentA studentA ;
@Test
public void testA(){
    String v= studentA.test();
    log.info("testa={}",v);
}
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: 
Error creating bean with name 'studentA': Bean with name 'studentA' has been injected into other beans [studentC] in its raw version as part of a circular reference, but has eventually been wrapped. 
This means that said other beans do not use the final version of the bean. 
This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.

3, Solve the exception

A,B,C Of the three classes , Of at least one class field Must be added @Lazy  

@Service
@Slf4j
public class StudentA {

    @Autowired
    @Lazy
    private StudentB studentB;
    @Async
    public String test(){
        return studentB.test();
    }
    @Async
    public String test1(){
        return "Studenta1";
    }
}

@Slf4j
@Service
public class StudentB {

    @Autowired
    @Lazy
    private StudentC studentC;
    @Async
    public String test() {
        return "Studentb";
    }
    @Async
    public String test1() {
        return studentC.test1();
    }
}

@Slf4j
@Service
public class StudentC {

    @Autowired
    @Lazy
    private StudentA studentA;
    @Async
    public String test() {
        return studentA.test();
    }
    @Async
    public String test1() {
        return "Studentc1";
    }
}

Reference resources :https://my.oschina.net/tridays/blog/805111 

Solutions to eliminate circular dependencies

Here, according to our service architecture, we will introduce how to avoid circular dependence , We use spring cloud Family bucket , The registry uses consul, The configuration center has config and apollo.

1, see bean Dependence

spring-cloud Provides bean Information viewing function , adopt ip: port +/actuator/beans You can see ( How to configure actuator It's not discussed in detail here ), Here's the picture , We can see each one bean Rely on the bean
 

  

2, analysis bean And its dependence beans

Above , We can look at some bean The dependencies of , thus , We can recursively find bean Whether there is a circular reference .

bean Information model

@Data
public class ApplicationBeans {

    private Map<String, ContextBeans> contexts;
}



@Data
public class ContextBeans {

    private Map<String, BeanDescriptor> beans;
    private String parentId;
}

 

 3, Code implementation

Here the test item is named alarm-center, The detection class is CheckCyclicDependenceService 

@Slf4j
@RefreshScope
@Service
public class CheckCyclicDependenceService {

// Service discovery @Autowired
private DiscoveryClient discoveryClient;
// Default detection api,admin Two projects , If you want to test other items , Can be found in config To configure @Value(
"${check-serviceids-value:api,admin}") private String checkServiceIds;

// entrance
public void checkCyclicDependence() { if (Strings.isNullOrEmpty(checkServiceIds)) { return; } List<String> serviceIds = Arrays.asList(checkServiceIds.split(",")); for (String serviceId : serviceIds) { long start = System.currentTimeMillis(); RestTemplate restTemplate = new RestTemplate();
// According to the service name consul Find a service instance ServiceInstance instance
= discoveryClient.getInstances(serviceId).get(0); String url = instance.getUri() + "/actuator/beans"; // be-all beans Information String applicationBeansStr = restTemplate.getForObject(url, String.class); ApplicationBeans applicationBeans = JSONObject.parseObject(applicationBeansStr, ApplicationBeans.class); long end = System.currentTimeMillis(); log.info("checkCyclicDependence get applicationBeans end,serviceid={},coust={}", serviceId, (end - start)); Map<String, ContextBeans> contexts = applicationBeans.getContexts(); Map<String, BeanDescriptor> qualifiedBeans = new HashMap<>(); for (Map.Entry<String, ContextBeans> conEntry : contexts.entrySet()) { if (!conEntry.getKey().startsWith(serviceId)) { continue; } ContextBeans contextBeans = conEntry.getValue(); Map<String, BeanDescriptor> beans = contextBeans.getBeans(); for (Map.Entry<String, BeanDescriptor> entry1 : beans.entrySet()) { String beanName = entry1.getKey().toLowerCase(); BeanDescriptor beanDescriptor = entry1.getValue(); if (!beanDescriptor.getType().startsWith("com.shuidihuzhu") || beanName.endsWith("aspect") || beanName.endsWith("controller") || beanName.endsWith("dao") || beanName.endsWith("datasource") || beanName.endsWith("fallback") || beanDescriptor.getDependencies().length == 0) { continue; } qualifiedBeans.put(entry1.getKey(), beanDescriptor); } } StringBuilder sb = new StringBuilder(); cyclicDependence(qualifiedBeans, sb); if (sb.length() > 0) { sb.append(System.getProperty("line.separator")); sb.append(" Re note code quality , Try to be non cyclic "); sb.append(System.getProperty("line.separator")); sb.append(" Services :" + serviceId); //alarmClient.sendByUser(Lists.newArrayList("zhangzhi"), sb.toString()); alarmClient.sendByGroup("cf-server-alarm", sb.toString()); end = System.currentTimeMillis(); } log.info("checkCyclicDependence end,serviceid={},coust={}", serviceId, (end - start)); } } public void cyclicDependence(Map<String, BeanDescriptor> beans, StringBuilder sb) { for (Map.Entry<String, BeanDescriptor> bean : beans.entrySet()) { String beanName = bean.getKey(); check(beans, beanName, beanName, 0, sb); } } public void check(Map<String, BeanDescriptor> beans, String beanName, String dependenceName, int depth, StringBuilder sb) { if (depth == 4) {// Here you can specify the depth , Too many levels , Easy stack overflow return; } depth++; BeanDescriptor bean = beans.get(dependenceName);// Dependencies of dependencies if (bean != null) { String[] deps = bean.getDependencies();// Dependencies for (String dep : deps) { if (dep.equals(beanName)) { sb.append(System.getProperty("line.separator")); String str = String.format("%s and %s There is a cyclic dependency ;", beanName, dependenceName); sb.append(str); log.info(str); } else { check(beans, beanName, dep, depth, sb); } } } } }

 

effect

We have a test environment job Patrol every few minutes , There is a circular dependence on the enterprise wechat alarm , Here we intercept a log , as follows :

 

Last

Drop insurance Mall - Architecture group recruitment java The intern

requirement :
1、 Have strong basic programming skills , Have a good command of JAVA programing language , Familiar with common data structures and algorithms .
2、 Learn about common open source frameworks and services (Spring,Netty,MySQL,Redis,Tomcat,Nginx etc. ).
3、 Familiar with network programming 、 Multithreaded programming 、 Distributed and so on .
4、 Read over spring Family source code priority , Technical blogs are preferred , be familiar with spring-cloud Technology stack first .
5、 Be proactive , Have strong executive ability and good communication ability .

 

 

版权声明
本文为[zzhi.wang]所创,转载请带上原文链接,感谢

Scroll to Top